由js闭包在循环中的使用引出的问题


Js传参方式

Js中的传参方式也有两种:by valueby sharing 这两种。说白了前者传值,后者传址。By value是对于原始数据类型,例如int,char之类的;而By sharing 和By reference是对于高级数据结构,如Object,struct之类。我们可以想象到一个Object或是struct 不能仅仅通过传值进行传参。当然啦,我们能够很容易明白 by sharingby value 的区别,但是 by sharingby reference 的区别却不一定能够明了,看下面一个例子:

var bar;
var foo = bar;
bar = {'key':'value'};
console.log(foo, bar);

// result
undefined Object {key: "value"} 

如上,By sharing 中foo 是undefined , bar 是{'key' : 'value'}; 而 By reference 则应该两者都是{'key' : 'value'}。

Js作用域

每个函数在创建完成时,它有三个重要的内置属性也同时被创建:

{
    AO      // 记录自己函数本身内的变量,参数等信息
    this    // 就是在调用 this.xx 时的那个家伙
    scope   // 指向外层函数 AO 的一个链(在实现的时候可能通过数组)
}

在Js中,我们经常讲的作用域 = SCOPE应该是这样的:
SCOPE = AO + scope

回到闭包问题上,下面的代码展示了最基础的循环引用问题:

for (var i=0; i<link.length; i++) {     // window scope
    link[i].onclick = function() {      // inner function
        alert(i);
    };
}

// inner function's SCOPE is as follow:
{
    AO
    this    // 等于 link[i]
    scope   // 指向 window 的记录,包括我们需要的变量 i
}

// for 循环的执行会立即执行完毕,所以当内层函数在 onclick 被触发时,
// inner function 查找变量 i 时,会在 AO + scope 中找,AO 中没有,
// 而 scope 中的变量 i 现在都已经成为 link.length 了,杯具了吧。

// 输出结果应该不用我讲了吧!:)

利用闭包写上面的程序那应该是:

for (var i=0; i<link.length; i++) {     // window scope
    link[i].onclick = (function(i) {    // outer function
        return function() {             // inner function
            alert(i);
        };
    })(i);
}

// inner function's SCOPE:
{
    AO      // no important info
    this    // we don't care it
    scope   // outer function & window scope
}

// outer function's SCOPE
{
    AO      // 包含参数 i
    this    // we don't care it
    scope   // window scope
}

这时,如果inner function被触发,他会从自己的AO以及scope(outer function的AO 和 window scope)中找寻变量i,可以看到outer function的AO中已经包含了i,而且对于这个for循环,会有对应有N个(function(){})() 被创建执行。所以每个inner function都有一个特定的包含了变量 i 的outer function。

左银右煌 /
Published under (CC) BY-NC-SA in categories programming  tagged with JavaScript