js中作用域,闭包问题

var data =[];
    for(var i=0;i<3;i++){
        data[i] = function(i){
            console.log(i);
        }
    }
    
    data[0]();
    data[1]();
    data[2]();

1、请问一下为何执行data[0]时是undefined?
2、for循环执行后,会分配3个空间,放着3个function吗?如果是,i的值会传进去吗?
3、执行结果是undefined,是因为执行完for循环完,没有3个空间,都释放了吗?还是其他原因
4、如果像传进去i,要用闭包(我已经试过了),是闭包会分配空间一直不释放吗?有其他方法吗?

阅读 3.5k
5 个回答

你这个例子跟闭包没有半毛钱的关系, 你的data数组中只是定义了三个函数,没有调用, 把你的代码改为下面的等价代码, 应该道理就很明显了

var data = []
for(var i=0; i<3; i++){
    data[i] = function (parameter) {
        console.log(parameter)
    }
}

data[0]()// 错误的调用,没有传参数,正确的调用应该是 data[0](argument)
data[1]()
data[2]()

这个例子的原理,发条橙子的作用域链解释得更清楚

如果你想体验闭包的话, 例子应该像下面这样

var data = []
for(var i=0; i<3; i++){
    data[i] = function () {
        console.log(i)
    }
}

data[0]() // 3
data[1]() // 3
data[2]() // 3

如果你想体验IIFE的话, 例子应该像下面这样

var data = []
for(var i=0; i<3; i++){
    data[i] = (function (i) {
            console.log(i)
    }(i));
}

data[0] // 0
data[1] // 1
data[2] // 2

如果你想体验闭包+IIFE的话, 例子应该像下面这样

var data = []
for(var i=0; i<3; i++){
    data[i] = (function (i) {
        return function () {
            console.log(i)
        }
    }(i));
}

data[0]() // 0
data[1]() // 1
data[2]() // 2

因为js中有了作用域链闭包的存在,所以会有上面第一段代码这样的输出,这往往不是开发者所预期和需要的结果,所以在ES5中,大家研究出了IIFE+闭包这种hack式的手法,来解决这个问题,使代码的运行结果更符合预期。

对于防止闭包的误用,ES6引入了块作用域(block)模块(module)箭头函数(arrow function), 可以减少部分对于IIFE+闭包的依赖

参考资料:
闭包: http://speakingjs.com/es5/ch0...
IIFE: http://speakingjs.com/es5/ch1...
IIFE in ES6: http://exploringjs.com/es6/ch...
IIFE in ES6: http://exploringjs.com/es6/ch...

第一次答题,如果有错误的地方,希望能指正。如果觉得有帮助,希望能点赞鼓励下^0^

  1. 因为data[0],data[1],data[2]都只是保存了函数

    function(i){  console.log(i); }

    调用时没有给形参传值,所以data[0],data[1],data[2]都是undefined;

    注意:函数的i是形参,不是for()里的变量i;你可以调用 data[0](1)(这里传入了参数1,结果是1)来验证我的说法。

  2. for循环结束后,data里的每个元素都变成了function(i){ console.log(i); }。可以通过console.log(data[0])、console.log(data[1])、console.log(data[2])来验证。

  3. 不是。理解了我说的第1和第2条回答,你就懂了。

  4. 如果你用闭包,是能正确打印出0,1,2的。如改成

    var data =[];
        for(var i=0;i<3;i++){
            data[i] = (function(i){
                return function(){
                    console.log(i);
                }
            })(i);    //自调用,传入了变量i
        }
        data[0]();// 打印出0
        data[1]();// 1
        data[2]();//2

有这个误区是作用域还没搞明白。
来分析一下作用域把。

var data =[];
for(var i=0;i<3;i++){
    // window作用域 含有 data,i
    data[i] = function(i){
        // 匿名函数作用域 包含一个参数变量 i。此时的作用域链关系是 全局(data,i)→当前(i);
        console.log(i); //所以这个i引用的是匿名函数的i 
    }
}
data[0](); // 没有传递参数 所以函数用undefined补全了 实质 function(undefined){console.log(i)}
data[1]();
data[2]();

你分配了三个空间,这个三个匿名函数是独立的

  1. 因为 i 的值没有传入 函数调用时候,(),空参数的

  2. 一个是函数的参数叫 i, 一个是有一个变量叫 i,不一样的东西

  3. 同第一个...

  4. 没听懂...

你可以console.log(data);看一下,data里边会有3个匿名函数,在你循环定义的时候创建3个匿名函数,同时函数的参数为i,执行的时候console.log(i),最后你调用的时候并没有传参数(i)所以是undefined! 最后你可以试一下data[0](0);data[1](1);data[2](2);

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题