关闭混乱

时间:2012-11-03 00:35:28

标签: javascript

我对这个关闭事情感到困惑。我下面有两个单独的代码看起来相似,但它们的输出是不同的。

function setup(x) {
var array = [];
for(var i=0;i<arguments.length;i++){
    array[i]= arguments[i];
}
return array;
}
console.log(setup('a','b'));  // will output ["a","b"] 

--------------
function f() {
var i, array = [];
for(i = 0; i < 3; i++) {
    array[i] = function(){
        return i;
    }
}
return array;
}

var a = f();                 
console.log(a());       //output: [function(),function(),function()]
console.log(a[0]());    //output: 3 //same output in a[1]() and a[2]() calls as well

现在我的问题是,输出结果如何不同?上面的两个代码都返回一个数组。在第一个代码中,它正确打印数组中的所有元素,而在第二个代码中,为什么不打印[1,2,3] ???

2 个答案:

答案 0 :(得分:4)

在第二个示例中,您在循环中创建3函数,但所有函数都在同一变量范围内创建,因此它们都引用并返回相同i变量的值

因此,从函数返回的i的值表示调用函数时的值。因为您在循环之后调用它们i的值为3,所以这是返回的值。

这就是封闭的含义。这些函数“关闭”创建它们的变量范围中存在的变量。它们不会关闭变量的,而是关闭变量本身,因此它们总是得到变量的当前状态。

对于引用i的不同值的每个函数,需要在具有自己唯一i的单独变量范围中创建每个函数。

因为在JavaScript中创建新变量作用域的唯一方法是调用函数,所以需要在新函数调用中创建每个函数。

function makeFunction(j) {
    return function(){
        return j;
    };
}

function f() {
    var i, array = [];
    for(i = 0; i < 3; i++) {
        array[i] = makeFunction(i);
    }
    return array;
}

所以我在这里创建了一个名为makeFunction的新函数。它接收一个参数,并返回一个引用并返回该参数的新函数。

因为makeFunction的每次调用都会创建一个新的唯一变量作用域,所返回的每个函数都将引用其自己唯一的j变量,因此将返回存在的j值调用时调用makeFunction(除非您的函数修改j,如果您愿意,可以执行此操作)

请注意,为清晰起见,我使用了变量名j。您也可以使用i或其他名称。

答案 1 :(得分:0)

在JavaScript中,您有函数语句和函数表达式。第一个声明命名函数,后者计算命名或匿名函数。你正在使用函数表达式。

我认为您想要做的是调用表达式。您所要做的就是立即调用返回i的函数。

function f() {
    var i, array = [];
    for (i = 0; i < 3; i++) {
        // The extra parentheses around the function are unnecessary here.
        // But this is more idiomatic, as it shares syntax with how function
        // expressions are introduced in statements.
        // I.e. you could just copy-paste it anywhere.
        array[i] = (function () {
            return i;
        })(); // don't rely on automatic semicolon insertion
    }
    return array;
}

另请注意,在您的问题示例中,所有闭包都返回3,因为它们都捕获相同的变量i

编辑:为了使前一段更清晰,如果你真的想拥有3个不同的闭包,你必须为每个闭包创建一个新的范围。与其他语言不同,Javascript不会仅通过打开块来创建范围。它只在函数中创建范围。这有两种方法:

function f() {
    var i, array = [];
    for (i = 0; i < 3; i++) {
        // With a parameter in an inner function
        array[i] = (function (n) {
            return function () {
                // A new n is captured every time
                return n;
            };
        })(i);
    }
    return array;
}

function f() {
    var i, array = [];
    for (i = 0; i < 3; i++) {
        array[i] = (function () {
            // With a variable in an inner function
            var n = i;
            return function () {
                // A new n is captured every time
                return n;
            };
        })();
    }
    return array;
}

然而,下一个例子是错误,因为即使在n块中声明了for,它也就好像它已在顶部声明一样该函数只在for块中初始化。请记住,这是JavaScript,而不是Java,不是C,不是C#,不是&lt;无论是括号中的语言&gt;:

function f() {
    var i, array = [];
    for (i = 0; i < 3; i++) {
        var n = i;
        array[i] = function () {
            return n;
        };
    }
    return array;
}

等效代码:

function f() {
    var i, array = [];
    var n;
    for (i = 0; i < 3; i++) {
        n = i;
        array[i] = function () {
            return n;
        };
    }
    return array;
}