JavaScript 变量、作用域、和内存问题学习笔记

Posted by Mars 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复制到新空间中,从此mars1mars2这两个变量互不干扰。

1.3.2 引用类型值的复制

var mars1 = new Object();
var mars2 = mars1; // 复制mars1对象
mars1.name = "mars1";
alert(mars2.name); //mars1

mars2复制了mars1之后,实际上只是指向mars1所指向对象的一个指针被复制到mars2的新内存空间中,并不是mars1指向对象的实际值。

所以复制之后,mars1mars2共同指向同一个堆内存中的对象,所以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的内存地址作为值传给objmars1中运算,增添了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()来检测nullobject都会返回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以便使其脱离工作环境让垃圾回收器回收

previousPost nextPost
已经有 1000000 个小伙伴看完了这篇推文。