整合下对作用域及闭包的理解

in JS基础 with 0 comments, 3409 views

先看个容易理解的:

//情形一

alert(a); //弹出undefined

var a = 5;
alert(a); //弹出5

//情形二

alert(a); //直接报错Uncaught ReferenceError: a is not defined

tips:造成这两种结果的原因很简单,当执行alert(a);语句时JavaScript实际上会沿着作用域链查找变量a最近定义的值,情形一中能够找到变量a的声明,但在执行第一句alert(a)时,a中还没有赋值,即为默认的undefined,当执行第二句alert(a)时,a已经被赋值为5,自然弹出5; 情形二中,由于找不到变量a的声明,所以直接报错中断JavaScript执行。

接下来再看个函数内部的:

//情形一

var name = 'window';

function getName() {
    var name = 'function';
    return name;
}
alert(getName()); //弹出function

//情形二

var name = 'window';

function getName() {
    return name;
}
alert(getName()); //弹出window

tips:画个示意图

当调用getName方法时会引用变量name,首先会搜索作用域链前端,如果在getName函数找到变量name的声明(情形一)搜索动作停止,返回getName函数内定义的name值;否则继续往上层父环境全局window搜索并返回全局变量name(情形二)。

继续深入:

闭包是与作用域相关的一个概念,它指的是内部函数即使在外部函数执行完成并终止以后,仍然可以访问其外部函数的变量。//还是先看一个简单的有趣小例子autoAdd = (function (){var num = 0;return function(){alert(num++);}})();autoAdd(); //弹出0autoAdd(); //弹出1autoAdd(); //弹出2

tips:呵呵,神奇吧,函数内的局部变量num似乎被保留了下来而并没有被立即销毁掉;这里引用一些概念,当某个函数被第一次调用时,会创建一个执行环境(execution context)对应此处的autoAdd execution context,每个执行环境都有一个与之关联的变量对象(环境中定义的所有变量和函数或者属性和方法都存在这个对象中,对于函数来说则将其活动对象activation object作为变量对象),代码在这个执行环境中执行时,会创建一个作用域链(作用域链本质上量个指向变量对象的指针列表,在链的最前端是当前执行代码所在环境的变量对象).

一般来讲,当函数autoAdd执行完毕后,对应的局部活动对象就会被立即销毁;闭包的情况有些特殊,内部匿名函数返回时(我的理解是此时外部函数autoAdd并未执行完毕),这个内部匿名函数会将外部函数autoAdd的活动对象加入到自己的作用域链中,也就是内部匿名函数仍然在引用这个活动对象,所以当外部函数autoAdd执行完毕后,虽然其执行环境和作用域链会被销毁,不过其活动对象与内部匿名函数存在引用所以没有被立即销毁,继续留在了内存中,直到匿名函数销毁。

最后举个实际应用的例子:

javascript:

var oDIV = document.getElementById('cmg');
var aA = oDIV.getElementsByTagName('a');
for (var i = 0; i aA[i].onclick = (function(num) {
        return function() {
            alert(num + 1);
        }
    })(i)
}

html:

<div id="cmg"><a id="a1" href="#">1</a>

<a id="a2" href="#">2</a>

<a id="a3" href="#">3</a></div>

参考资料《JavaScript高级程序设计(第3版)


Responses ${replyToWho} / Cancel Reply