JavaScript 变量、作用域、和内存问题学习笔记
Posted by Mars . Modified at
《JavaScript 高级程序设计第四章》:变量值的基本类型和引用类型、类型检测、执行环境、作用域链、垃圾收集。
这应该是JavaScript最重要的部分,理解这些概念真的很重要。————2017.2.8 Mars 北航三馆314教研室
1. 变量
1.1 JavaScript 的松散变量类型
JavaScript的变量是松散类型
的:变量名称只是用于在特定时间内保存特定值的一个名字,并非定义某一类型变量必须存放该类型的数据。因此同一变量名的变量值和类型都可以在生存周期内被改变。
1.2 变量值的基本类型与引用类型
基本类型值:简单的数据段。(undefined\null\boolean\number\string),按值访问,操作的是保存在变量中的实际的值。
引用类型值:保存在内存中的对象。JavaScript 不支持直接访问内存空间,操作对象访问的都是对象的引用。
什么是引用?
引用可以理解为对象的另一个复制品,并且与对象捆绑,同时更改或变化。(就是对象的分身)
1.3 变量值的复制
1.3.1 基本类型值的复制
var mars1=5;
var mars2=mars1;
创建mars2
之后,内存为其分配了新空间,并把mars1
的值5和类型number
复制到新空间中,从此mars1
和mars2
这两个变量互不干扰。
1.3.2 引用类型值的复制
var mars1 = new Object();
var mars2 = mars1; // 复制mars1对象
mars1.name = "mars1";
alert(mars2.name); //mars1
mars2
复制了mars1
之后,实际上只是指向mars1
所指向对象的一个指针被复制到mars2
的新内存空间中,并不是mars1
指向对象的实际值。
所以复制之后,mars1
与mars2
共同指向同一个堆内存
中的对象,所以name属性
被同时修改了。
1.4 变量值向函数参数的传递
ECMAScript 所有函数的参数都是按值传递
的。
1.4.1 基本类型值的传递
基本类型值在传递过程和基本类型值复制过程一样。函数内部对参数的操作不影响函数外部变量本体。
1.4.2 引用类型值的传递
引用类型值在传递给函数参数的时候是按值传递的,这个值是原对象的内存地址,因此函数参数在函数中的操作可以直接影响到外部的变量本体。
function mars1(obj){
obj.name = "mars";
}
var mars2 = new Object();
mars1(mars2);
alert(mars2.name); // mars
obj
是函数mars1
的参数(局部变量),mars2
的内存地址作为值传给obj
在mars1
中运算,增添了name
属性,实际上就是增添了堆内存中mars2
所代表对象的name
属性。
function mars1(obj){
obj.name = "mars";
obj = new Object();
obj.name = "whatthehell";
}
var mars2 = new Object();
mars1(mars2);
alert(mars2.name); //mars
上述例子表明,这种传递并不是按引用传递
。
如果是按引用传递,那么mars2
的指向对象会随着obj
(mars2指针)的重新赋值而随之指向新的对象,实际上并不会。
1.5 类型检测
1.5.1 typeof() 操作符
typeof() 操作符可以用来检测基本类型值。
typeof()
操作符是确定变量是string、number、boolean、undefined的最佳方法。用typeof()
来检测null
和object
都会返回Object
。
1.5.2 instanceof 操作符
用于检测引用类型值,instanceof 根据原型链来识别。
alert(person instanceof Object); // true
alert(colors instanceof Array); // true
alert(pattern instanceof RegExp); // true
所有引用类型的值都是Object
的实例。
2. 执行环境与作用域
2.1 执行环境
执行环境
定义了变量或函数有权访问的其他数据,决定了它们各自的行为。
每个执行环境都有一个与之关联的变量对象
,环境中定义的所有变量和函数都保存在这个对象中。
全局环境是最外层的执行环境。Web浏览器中是Windows对象,全局环境中的变量只有在应用程序退出(关闭浏览器或者网页)才会销毁。
每执行一个函数,函数的执行环境就会被推入一个环境栈
中,函数执行完毕,栈将其环境弹出,把控制权交给之前的执行环境。
2.2 作用域链
作用域链
的作用是保证对执行环境有权访问的所有变量和函数的有序访问。
作用域链的前端是当前执行的代码所在环境的变量对象,最后端是全局环境的变量对象。
2.3 标识符解析
沿着作用域链一级一级搜索标识符的过程,从前端开始,一直到找到标识符为止。
2.4 if 和 for 语句中定义变量的注意事项
var x=true;
if(x){
var y="mars";
}
alert(y); //mars
这里if语句内定义的变量y被保存在当前的执行环境(全局)中,所以在{}外面也可以访问。
for(var i=0;i<10;i++){
...
}
alert(i);
这里i在for循环内定义,结束后并不会释放,因为在全局环境中。
3. 垃圾收集与内存管理
垃圾收集的两种方式:标记清除和引用计数。
所有浏览器都是用标记清除式的垃圾收集策略。
内存管理
:对所有的全局变量,在不再有用的时候应该设置为null以便使其脱离工作环境让垃圾回收器回收