简体   繁体   English

封闭混乱

[英]Closure confusion

I am having some confusion regarding this Closure thing. 我对这个Closure事情有些困惑。 I have two separate codes below that look similar but their output are different. 我在下面有两个单独的代码,看起来很相似,但是它们的输出却不同。

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

Now my question is, how come the output is different? 现在我的问题是,输出为何不同? both of the codes above return an array. 上面的两个代码都返回一个数组。 in the first code, it prints all elements in array correctly whereas in the second code, why doesn't it print [1,2,3]??? 在第一个代码中,它正确打印了数组中的所有元素,而在第二个代码中,为什么不打印[1,2,3]?

In your second example, you are creating 3 function in a loop, but all the functions are created in the same variable scope, so they all reference and return the value of the same i variable. 在第二个示例中,您正在循环中创建3函数,但是所有函数都是在同一变量范围内创建的,因此它们都引用并返回相同i变量的值。

Therefore the value of i returned from the functions represents the value at the time when the functions are invoked. 因此,从函数返回的i的值表示调用函数时的值。 Because you're invoking them after the loop, the value of i is 3 , so that's the value returned. 因为您是在循环调用它们,所以i值为3 ,所以这就是返回的值。

This is what is meant by closure. 这就是闭包。 The functions "close over" the variables that existed in the variable scope where they were created. 函数“关闭”了变量创建区域中存在的变量。 They do not close over the value of the variables, but rather then variables themselves, so they always get the current state of the variable. 它们不会覆盖变量的 ,而是会覆盖变量本身,因此它们始终会获取变量的当前状态。

For each function to reference a different value of i , each function would need to need to be created in a separate variable scope that has its own unique i . 对于每个引用不同i值的函数,都需要在具有自己唯一i的单独变量范围中创建每个函数。

Because the only way to create a new variable scope in JavaScript is to invoke a function, you'll need to create each function within a new function invocation. 因为在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;
}

So here I made a new function called makeFunction . 所以在这里,我做了一个叫做makeFunction的新函数。 It receives a single parameter, and returns a new function that references and returns that parameter. 它接收一个参数,并返回一个引用并返回该参数的新函数。

Because each invocation of makeFunction creates a new and unique variable scope, each function returned will be referencing its own unique j variable, and will therefore return the value of j that existed when makeFunction was invoked (unless your function modifies j , which it could do if you wanted) . 由于makeFunction每次调用makeFunction创建一个新的唯一变量范围,因此返回的每个函数都将引用其自己的唯一j变量,因此将返回调用makeFunction时存在的j的值(除非您的函数修改j ,否则它可以这样做)如果您想)

Note that I used the variable name j for clarity. 请注意,为清楚起见,我使用了变量名j You could use i as well, or some other name. 您也可以使用i或其他名称。

In JavaScript, you have function statements and function expressions . 在JavaScript中,您具有函数语句和函数表达式 The first declare named functions, the latter evaluate to a named or anonymous function. 前者声明命名函数,后者评估为命名或匿名函数。 You're using a function expression. 您正在使用函数表达式。

What I think you want to do is a call expression. 我想您想要做的是一个调用表达式。 All you have to do is to immediately call your function that returns i . 您要做的就是立即调用返回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;
}

Also note that, in your problematic example, all closures return 3 because all of them capture the same variable i . 还要注意,在您遇到问题的示例中,所有闭包都返回3因为它们都捕获了相同的变量i

EDIT: To make the previous paragraph more clear, if you really wanted to have 3 distinct closures, you'd have to create a new scope for each one. 编辑:为了使上一段更加清楚,如果您确实想拥有3个不同的闭包,则必须为每个闭包创建一个新范围。 Unlike other languages, Javascript doesn't create scope just by opening a block. 与其他语言不同,Javascript不仅仅通过打开一个块来创建作用域。 It only creates scope in functions. 它仅在函数中创建作用域。 Here are two ways of doing this: 这有两种方法:

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;
}

The next example, however, is wrong , because even though n is declared in the for block, it will be as if it has been declared at the top of the function and only initialized in the for block. 但是,下一个示例是错误的 ,因为即使nfor块中声明了,也好像它已经在函数的顶部声明并且仅在for块中初始化了一样。 Remember, this is JavaScript, not Java, not C, not C#, not <whatever bracketed language>: 请记住,这是JavaScript,不是Java,不是C,不是C#,不是<任何括号内的语言>:

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

Equivalent code: 等效代码:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM