作用域、闭包等概念的理解

总结一下我对JS中这些基本却略纠结的概念的理解。

作用域

我们知道,JS不支持块级作用域,只支持函数作用域。函数体内,既不是局部变量,也不是参数的变量称为自由变量。如果没搞清楚函数的作用域,有时某些自由变量的值会与你所想的很不一样。举个简单例子

 1 var a = 10;
 2 
 3 function getA() {
 4     alert(a);
 5 }
 6 
 7 (function() {
 8     var a = 20;
 9     getA();     //10         
10 })();    
11 
12 (function(fn) {
13     var a = 20;
14     fn();       //10,作为参数传入也是一样
15 })(getA);    

先说说执行上下文,也就是每一步的执行环境。执行上下文里有变量对象作用域链this

变量对象存储着定义在上下文中的函数声明和变量,相当于保管数据。

作用域链指定了各级作用域的优先顺序,比如在当前上下文中的变量对象没有找到,就会去父上下文中去找,顺着作用域链一直向上找。

this是执行上下文的一个属性,很多地方说this是函数或构造函数的属性的说法是错误的。在进入上下文的时候确定了this的值,指向调用该函数的对象,this的值不会为null,所以如果没有明确调用该函数的对象那么this指向window,在整个上下文持续期间this的值不变。this不是变量,所以给this赋值是无效的。

函数的作用域scope=AO+[[scope]]

当函数被激活调用时,函数上下文中的变量对象称为活动对象AO,它除了存储定义在函数里的变量和函数之外,还包括传递给函数的参数。活动对象在函数被调用时创建。[[scope]]是函数的一个内部属性,它是所有父级变量对象的层级链,注意是所有父级,包括父的父级。函数被创建时就获得了[[scope]]属性,[[scope]]是静态的,也就是说在定义函数时就有它了,而且保持不变,不管函数有没有被调用,直到函数被销毁为止,[[scope]]属性都保持不变。注意,活动对象是和执行上下文相关的,而[[scope]]是函数的一个内部属性,它们结合在一起构成了函数的作用域。

 

闭包

官方定义看起来比较高大上,闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。换句话说,闭包是代码块和创建该代码块的上下文中数据的结合(详见汤姆大叔深入理解javascript系列)。如果你对上面作用域理解了,那么你可能会问,那JS中的所有函数岂不都是闭包吗。是的,理论上说JS中的所有函数都是闭包,因为函数创建时就把父上下文的数据保存在[[scope]]里了,即使是在window下创建的函数,按照定义它也是闭包。有点像在玩文字游戏,主要是为了加深理解。

从实践角度,我们通常所说的闭包,我的理解是函数保留了对父上下文中变量的引用,简单说就是引用了自由变量,那么即使被引用变量所在的上下文销毁了,这个变量没有被GC回收,仍存在于函数的作用域链中;或者函数由父上下文返回被外部的变量引用了,即使创建函数的上下文销毁了,函数仍然存在。这样的函数,叫做闭包。换言之,其作用在于使变量和函数不会正常地被GC回收而可以通过闭包的作用域访问到。

需要注意的是,同一个父上下文中创建的闭包共用一个[[scope]]属性,这是理所当然的,也就是说某一个闭包对它的[[scope]]里的变量进行了修改,那么其它闭包的[[scope]]里相应的变量也会变化,道理大家都懂,但初学时在实际应用中还是容易犯错误。

 1 var data = [];
 2 
 3 for (var i = 0; i < 3; i++) {
 4     data[i] = function() {
 5         alert(i);
 6     };
 7 }
 8 
 9 data[0]();   //3,而不是0
10 data[1]();   //3,而不是1
11 data[2]();   //3,而不是2    

 

原型链

原型的知识网上书上太多了,这里说一下prototype和__proto__的区别。

prototype是函数的一个属性,JS中每个函数都有这个属性,它指向一个对象,就是我们常说的原型。

__proto__是对象的一个内部属性,所谓内部就是本意是不会被外部看到的,是JS内部用来查找原型链的,只不过某些浏览器(chrome等)将其暴露出来了。

1 function Person() {}
2 
3 var p = new Person();
4 
5 alert(p.__proto__ === Person.prototype);   //true

 

posted @ 2015-08-11 17:02  coIorZ  阅读(1754)  评论(2编辑  收藏  举报