检测类型
- typeof
var a = 'Nicholas'
var b = null
var c = new Object()
console.log(typeof a) // String
console.log(typeof b) // Object
console.log(typeof c) // Object
typeof 操作符确定一个变量是字符串、数组、布尔、还是undefined的最佳工具。如果变量的值是一个对象或者null,则都只会返回Object
- instanceof
因此在进行复杂类型的检测是,可以使用instanceof。
// 语法如下
// result = variable instanceof constructor
// 实例
console.log(person instanceof Object)
console.log(list instannceof Array)
如果变量给定引用类型(根据他的原型链来识别)的实例,根据规定,所有引用类型的值都是Object 实例。 因此在检测一个 应用类型的值和Object构造函数,返回的始终时 true。时instanceof操作符检测函数时,返回的时‘function’。
作用域
作用域分为全局和局部,总的的来说子级作用域可以访问父级作用域。父级无法访问子级作用域。由子级非向父级,局部向全局的作用域规则就是作用域链
- js没有块级作用域
if (true) {
var color = 'cyan'
}
console.log(color) // cyan
关于没有作用域,DOM操作元素的经典案例
// 获取 同类元素注册同一事件
var btns = document.getElementsByTagName('button') // 获取所有的button元素
// 循环注册事件
for(var i = 0; i < btns.length; i++) {
var item = btns[i]
item.onclick = function() { // 点击按钮 弹框当前索引
alert(i)
}
}
以上,当点击按钮时会发现,每一个按钮的值都是最后一个。简单来说这是因为 var 声明的的没有作用域,当事件触发的时候,去访问i,因为触发事件之前所有的事件都已经注册完毕,所有这时候i是btns的长度
解决方案
// 1、闭包独立作用域
var btns = document.getElementsByTagName('button')
for (var i = 0; i < btns.length; i++){
(function(i) {
var item = btns[i]
item.onclick = function() {
alert(i)
}
})(i)
}
// 2、ES6 let申明变量
var btns = document.getElementsByTagName('button')
for (let i = 0; i < btns.length; i++){
var item = btns[i]
item.onclick = function() {
alert(i)
}
}
- 隐式全局变量
没用 标识符申明的变量。默认是全局变量
funtion fn(num1, num2) {
sum = num1 + num2
return sum
}
var res = fn(1, 1) // 2
console.log(sum) // 2
- 查询标识符
当在某个环境中引用某一个 标识符时。就会向上(作用域底端)逐级查询给定名字匹配的标识符。如果找到了,则搜索过程停止。反之则意味着变变量未定义。
局部作用域没有,向上找
var color = 'cyan'
function getColor() {
console.log(color)
}
getColor() // cyan
当前作用域存在
var color = 'cyan'
function getColor() {
var color = 'violet'
return color
}
console.log(getColor()) // violet
-
垃圾收集
JavaScript 具有自动垃收集机制。当在函数中申明一个局部变量,会为局部变量在栈(或堆)内存上分配相应的空间,以便存储他们的值。在函数使用结束后。此时局部变量就没有存在的必要了。因此就可以释放他们的内存。 在这种情况下很容易可以判断出变量是否还在存在的必要;但是并非所有情况都是如此容易得出结论,具体到浏览器中的实现,通常有两个策略
- 标记清除
JavaScript中最常见的垃圾收集方式是标记清除。当变量进入环境(例如在函数中申明一个变量),就将这个变量标记为进入环境
。从逻辑上来讲,永远不能释放进入环境的变量所占用的内存。当变量离开环境时,就将其标记为离开环境
。
可以使用很多方式来标记变量。比如,有一个“进入环境的”的变量列表以及一个“离开环境的”变量列表。如何标记变量其实并不重要,关键在于采取什么策略。
- 引用计数
另一种不太常见的垃圾收集策略叫做引用计数。引用计数的含义是跟踪记录每个值被引用的次数。- 当申明一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1.
- 如果同一个值又被赋值给另一个变量,则该值的引用次数加1。
- 如果包含对这个值引用的变量又取得了另一个值,则这个值的引用次数减1。
当引用次数变成0时,则说明没有办法再次访问这个值了,因此就可以将其占用的内存空间回收回来
循环引用问题
- 标记清除
function problem() {
var objectA = new Object()
var objectB = new Object()
objectA.someOtherObject = objectB
objectB.anotherObject = objectA
}
在这个例子中,两个对象分别用两个数据 进行互相引用;也就是说这两个对象的引用次数都是2。当函数执行完毕后,这两个对象会依然存在,因为他们的引用次数永远不会是0。并且如果多从调用,会导致大量内存得不到回收
- 性能问题
垃圾收集器是周期性运行的,而且如果为变量分配的内存数量很可观,那么回收工作也是相当大的。在这种情况下,确定垃圾收集的事件间隔是一个非常重要的问题。
IE的垃圾收集例程
最初,IE的垃圾收集器是根据内存分配量运行的,具体一点说,就是256个变量、4096个对象(数组)字面量或数组元素(solt)或者64KB的字符串。到达上述任何一个零界值,垃圾收集器就会运行。
那么这种方式的严重弊端就是,如果一个脚本中包含多变量,改脚本的很可能在其生命周期中一直保有那么多的变量。这样一来垃圾收集器就不得不频繁工作运行。结果,由此引发的严重性能问题初始IE7重写了其垃圾收集例程
随着IE7的发布,其JavaScript引擎的垃圾收集例程改变了工作方式:触发垃圾收集的变量分配、字面量和(或)数组元素的临界值被调整至被调整为动态修正。IE7中的各项临界值在初始时与IE6相等。如果垃圾收集回收回收的内存分配量低于15%,则变量、字面量和数组元素的临界值就是加倍。如果例程回收了85%的内存分配量,则将各种临界值重置回默认值。这样看是简单的调整,极大的提升了IE在运行大量JavaScript页面的性能。
小结
基本类型引用类型:
- 基本类型值在内存中占据固定大小的空间,因此被报存在栈内存中。
- 从一个变量向另一个变量复制基本类型的值,会创建这个值的副本
- 引用类型的值是对象,保存在堆内存中
- 包含引用类型的变量实际上包含的并不是引用类型本身,而是一个执行该对象的指针
- 从一个变量向另一个变量复制引用类型的值,复制的其实是指针,因此两个变量最终指向同一个对象。
- 确定一个值是哪种基本类型可以使用
typeof
操作符,而确定一个值是哪种引用类型可以使用instanceof
操作符
执行环境:
- 执行环境有全局执行环境(全局环境)和函数执行环境之分
- 每次进入一个新执行环境,都会创建一个用于搜索变量和函数的作用域链
- 函数的局部环境,有权访问其包含(父)环境,乃至全局环境
- 全局环境只能访问在全局环境中定义的变量和函数,而不能直接访问局部环境中的任何数据
- 变量的执行环境有助于确定应该何时释放内存
垃圾收集例程:
- 离开作用域的值将被标记为可回收垃圾,因此将在垃圾收集期间被删除
- “标记清除”是目前主流的垃圾收集算法,这种算法的思想是给当前不使用的值加上标记,然后再回收其内存
- 另一种垃圾收集算法是“引用计数”,这种算法的思想是跟踪记录所有值被引用的次数。JavaScript引擎目前都不在使用这种算法;
- 当代码中存在循环引用现象时,“引用计数”算法会导致问题
- 解除变量的引用有助于消除循环引用对象。而且对垃圾的收集也有好处。为了确保有效地回收内存,可以及时解除不再使用的全局对象、全局对象属性以及循环引用变量的引用。
网友评论