第四章 变量、作用域与内存问题
1.基本类型与引用类型
基本类型 | 引用类型 |
---|---|
Undefined | Array |
Null | Object |
Boolean | function |
Number | … |
String | |
简单的数据段,占据固定大小空间(保存在栈内存中) | 多个值构成的(类似结构体)(保存在堆内存中) |
按值访问:可以直接操作变量的值 | 操作对象时其实是在操作对象的引用 |
传递参数:值传递 | 与指针传递类似 |
2.复制
①基本类型(创建值的副本,存到另一个变量中)
②引用类型(两个变量指针指的是同一个对象)
3.检测类型的操作符
①确定一个值是哪种基本类型(typeof)
注意:
1.typeof在检测引用类型值时用处不大
2.typeof检测函数时,会返回“function”
3.typeof的返回值类型为字符串,因此typeof(typeof(undefined))值为string。
②确定一个值是哪种引用类型(instanceof)
4.变量的作用域
作用域链:(从前到后)当前执行代码所在的环境变量对象—>包含之前环境的下一个环境对象—>。。。—>全局 执行环境的变量对象(一层套一层的父子结构)
标识符解析:延作用域链一级一级搜索标识符
例子所对应的作用域链:(子可以访问父的变量和函数;父不可以访问子的变量和函数)
①延长作用域链
执行到以下语句时可以在当前函数环境的作用域链的前端临时增加一个变量对象,该对象会在当前环境代码执行(不确定)后被移除。
with:将指定的对象添加到作用域链中
catch语句:创建一个新的变量对象,其中包含的是被抛出的错误对象的声明(不是很懂)
BuildUrl()函数可以访问with中定义的变量
②JavaScript没有块级作用域(与类C语言:C++, Java中不同的地方)
一句话:子结构中定义的变量在父结构中依旧有效(自己理解的)
Ⅰ.声明变量
用var声明是将变量添加到最接近的环境中(函数内部和with内部最接近的环境都是函数环境)
不用var声明是将变量添加到全局环境
注意:不用声明直接初始化变量可能会导致意外,建议初始化变量前一定要先声明。
Ⅱ.查询标识符
原则:按作用域链顺序一级一级查找,如果搜索到了,则立即停止。
注意:也就是说,父级中如果有相同名字的变量则不会被搜索。
getColor()函数外的color变量内容会在函数执行之后换成函数内部的color变量内容,因为先搜索到函数内部的color变量。
也就是说,任何位于局部变量color之后的的代码,不用window.color都访问不到全局的color变量。
如果一个变量是对象,另一个不是,就会在对象变量上调用valueOf()取得其基本类型的值,再通过规则比较。
注意:访问局部变量比访问全局变量更快,JavaScript用了这种方法优化标识符查询
5.垃圾收集方法
有自动垃圾收集器每隔一段时间执行一次,或者变量、对象、数组元素和字符串大小达到某个值执行一次(IE)。
①标记清除:垃圾收集器运行时,先对所有环境中的变量进行标记,然后去掉那些有用的变量标记,剩下标记的就 是应回收的变量内存。
②引用计数:跟踪记录每个值被引用的次数:当声明了一个变量并将一个引用类型值赋给该变量时,这个值的引用 次数就是1。如果同一个值又赋给另一个变量,该值的引用次数加1;如果包含对这个值引用的变量 又取得了另一个值,这个值得引用次数减1。当这个值的引用次数变成0时,就说明无法再访问这个 值,就可以将其占用的内存空间回收回来(不太懂,是回收什么,如果是回收含这个值的变量,那么 其实已经没有变量的值是这个值了,回收哪个呢)
该算法问题:(循环引用)
objectA和objectB这两个对象的引用次数都是2,在引用计数方法中,它们的引用次数永远不可能是0,所以这两个变量永远占用着内存。
注意:IE有一部分对象不是JavaScript原生对象,BOM和DOM中的对象就是使用C++以COM(组件对象模型)对 象的形式实现的,COM的垃圾收集机制就是引用计数,比如下面例子就会出现问题:
为避免循环引用的问题,最好在不使用它们时手动断生。
注意:IE9把BOM和DOM对象都转化成了真正JavaScript对象,从而避免了引用计数产生的问题。
优化内存:变量中数据不再有用,就将值设置为null
注意:IE9把BOM和DOM对象都转化成了真正JavaScript对象,从而避免了引用计数产生的问题。
优化内存:变量中数据不再有用,就将值设置为null