第四章 变量,作用域和内存问题
一. 变量类型
1.1 基本类型与引用类型
- 基本类型:
指简单的数据段。按值访问,可操作保存在变量中的实际值。包括Undefined,Null,Boolean,Number,String。 - 引用类型:
指可能由多个值构成的对象。按引用访问,Js不允许直接访问内存中的位置,实际操作的使对象的引用。
1.2 动态属性
对于引用类型,可为其添加,删除和修改属性和方法。
var person = new Object();
person.name = 'aa';
alert(person.name);
对于基本类型,可以进行赋值操作
var person = 'aa', people = 1234;
1.3 复制变量值
一个变量向另一个变量复制基本类型值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。
var num1 = 2;
var num2 = num1;
一个变量向另一个变量复制引用类型值,会将存储在变量对象中的值复制一份放到为新变量分配的空间中,这个值的副本实际上是一个指针,指向存储在堆中的一个对象,复制后,两个变量将引用同一个对象。改变一个变量会影响另一个变量。
var obj1 = new Object();
var obj2 = obj1;
obj1.name = 'aaa';
alert(obj2.name); // 'aaa'
1.4 传递参数
ECMAScript 中所有函数的参数对后世按值传递的。就是说函数传参过程中,形参实际使复制了实参,作为一个变量存在。不论要传递的参数是基本类型还是引用类型,形参进行的是变量复制的操作。(函数的参数可以想象为局部变量)
// 基本类型
function addTen(num) {
num += 10;
return num;
}
var count = 20;
var result = addTen(count);
alert(count); // 20,没变化
alert(result); // 30
// 引用类型 第一种
function setName(obj) {
obj.name = 'aaa';
}
var person = new Object();
setName(person);
alert(person.name); // 'aaa'
// 引用类型 第二种
function setName(obj) {
obj.name = 'aaa';
obj = new Object();
obj.name = 'bbb';
}
var person = new Object();
setName(person);
alert(person.name); // 'aaa'
1.5 检测类型
检测基本数据类型时,用typeof很合适;检测引用类型时可以使用instanceof
二. 执行环境及作用域
- 执行环境,定义了变量或函数有权访问的其他数据,决定它们各自的行为。每个执行环境都有与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。
- 全局执行环境,是最外围的一个执行环境。根据ECMAScript实现所在的宿主环境不同,表示执行环境的对象也不一样。web浏览器中,全局执行环境被认为是window对象,所有全局变量和函数都是作为window对象的属性和方法创建的。某个执行环境代码执行完毕,该环境被销毁,保存的变量和函数定义也会被销毁。全局执行环境直到程序退出才被销毁。
- 函数执行环境,当执行流进入一个函数,函数环境会被推入一个环境栈。执行之后,栈将其环境弹出,控制权交给之前的执行环境。这就是ECMAScript的执行流机制。
- 作用域链,当代码在一个环境中执行时,会创建变量对象的一个作用域链,保证对执行环境有权访问的所有变量和函数的有序访问。作用域链开端是当前执行代码所在环境的变量对象。如果环境是函数,则将活动对象作为变量对象。作用域链中的下一个变量对象来自包含环境,再下一个的变量对象来自下一个包含环境,直到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象。
2.1 延长作用域链
执行环境有两种,全局和局部(函数)。
可以通过try-catch的catch 和 with 语句延长作用域链。
function buildUrl() {
var qs = '?a=1';
with(location) {
var url = href + qs;
}
return url;
}
2.2 没有块级作用域
由非函数的花括号封闭的代码块中,没有块级作用域。未用var定义的变量使用后,会使变量作用域提升。
function a() {
if (true) {
c = 3;
var b = 2;
}
return b;
}
a(); // 2
console.log(c); // 3
变量搜索过程从作用域链的开端开始,向上逐级查询与给定名字匹配的标识符。如果在局部环境中找到,则搜索过程停止,变量就绪;如果没有找到,直到全局的环境变量。如果还没有,就意味变量未声明。
三. 垃圾收集
JS有自动垃圾收集机制。执行环境负责管理代码执行过程中使用的内存。不再继续使用的变量释放其占用的内存。
JS常用的方式使标记清除。当变量进入和离开环境时都会打上相应标志。垃圾收集器进行内存清楚工作。
另一种方法是引用计数,跟踪每个值的引用次数。赋值+1,包含这个值的变量取得另一个值,则-1。数量为0时被销毁。会存在未置成0的情况,所以标记清楚更好。
3.1 性能问题
垃圾收集器时周期性运行的。
有手动调用方法,不推荐 window.CollectGarbage()
3.2 管理内存
一旦数据不再有用,最好通过将其值设为null来释放其引用——解除引用法。适用于大多数全局变量和全局对象的属性。局部变量会在离开执行环境时自动被解除引用。解除引用不是指自动回收该值所占用的内存,而是让值脱离执行环境,便于垃圾收集器下次运行时回收。
参考:
- JS红宝书 第四章 变量,作用域和内存问题