且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Javascript闭包问题

更新时间:2023-09-02 19:35:22

在第一个示例中,您声明了一个本地变量中的数组元素的内容的副本:

  var item = items [i ]; 

内联注释说,这里我们记住闭包范围内的值。



在第二个示例中,您不必将 i 作为参数,也可以这样做:

 (function(){

var prop = i; //见这里!

控制台.log(property:+ i);
//为属性
创建一个nwe getter [get+ prop] = function(){
return properties [prop] ;
};

//为属性
创建一个新的setter [set+ prop] = function(val){
properties [prop] = val;
};
})();

这两个例子更类似。



同样,你也可以改变第一个例子来传递 i 作为参数,而不是在变量中详细地声明它。

 (function(item){
//将一个函数绑定到元素
obj [on+ items [item] = function
// item是指已成功的父变量
//在此循环的上下文中作用域
alert(感谢您的+ items [item]);
};
})(i);

对于是否使用声明变量的本地副本是任意的, var 语句,或将其作为参数传递到自执行函数中。



编辑:


$ b b

@Zecc在评论中买了一个好点,我想解释一下:

  i){

//使用`i'按预期工作

}(i));

其中:

 (function(){

var i = i;

//使用`i'不起作用... i = undefined。

}());

这是因为 var variable = value 语句实际上是:

 (function(){

var i;

i = i;

}());

var 关键字始终分配变量 undefined


So, I'm still reading Apress Pro Javascript Techniques and i'm having troubles with closures.

As John Resig states:

Closures allow you to reference variables that exist within the parent function. However it does not provide the value of the variable at the time it is created; It provides the last value of the variable withing the parent function. The most common issue under which you'll see this occurr during a for loop. There is one variable being used as an interaor (e.g., i). Inside of the for loop, new functions are being created that utilize the closure to reference the iterator again. The rpoblem is tat the time the new closured functions are called, they will reference the last value of the iterator (i.e., the last position in an array), not the value taht you woul expect.

Then he presents, in listing 2-16 an example using anonymous functions to induce scope.

/**
 * Listing 2-16. Example of Using Anonymous Functions to induce the 
 * Scope Needed to Create Multiple Closure-Using Functions 
 */
// An element with an ID of main
var obj = document.getElementById("main");

// An array of items to bind to
var items = ["click", "keypress"];

for (var i = 0; i < items.length; i++) {
    // Use a self executed anonymous function to induce scope
    (function() {
        // Remembre the value within this scope
        var item = items[i];

        // Bind a function to the element
        obj["on" + item] = function() {
            // item refers to a parent variable that has been successfully
            // scoped within the context of this loop
            alert("thanks for your " + item);
        };
    })();               
}

This example works as expected, and the behavious of the main object is correct.

The in the following, it uses another time a self-executing function to induce scope, during an iteration.

The purpose of the function is to create an object, defining getters and setters for all its properties. In this case, the example does not work.

/**
 * Listing 2-25. Example of Dynamicaaly Generated Methods That Are Created 
 * When a New Object is instantiated
 */          
// Create a new user object that accepts an object of properties
function User(properties) {
    // Iterate thorugh the properties of the object, and make sure
    // that it's properly scoped (sas discussed previously)
    var that = this;

    for (var i in properties) { 
       (function() {
           console.log("property: " + i);
           // Create a nwe getter for the property
           that["get" + i] = function() {
               return properties[i];
            };

            // Create a new setter  for the property
           that["set" + i] = function(val) {
               properties[i] = val;
           };
       })();                    
    }
}

// Create a new user object instance and pass in an object of
// properties to seed it with
var user = new User({
    name: "Bob",
    age: 44
});

// Just note that the name property does not exists, as it's private within the  
// properties object
alert(user.name == null);

// However, we're able to access its value using the new getnaem()
// method that was dynamically generated
console.log("name: " + user.getname());  // name: 44 :(
alert(user.getname() == "Bob");

// Finally, we can see that it's possible to set and gt the age using
// the newly generated functions

user.setage(22);
alert(user.getage() == 22);

Instead, after passing the i parameter as argument to the self-executing function,it works.

for (var i in properties) { 
       (function(prop) {
           console.log("property: " + i);
           // Create a nwe getter for the property
           that["get" + prop] = function() {
               return properties[prop];
            };

            // Create a new setter  for the property
           that["set" + prop] = function(val) {
               properties[prop] = val;
           };
       })(i);                   
    }

My question is:

  • Why in the first case (for loop), it is not necessary to pass the i parameter, while
    in the second (for in) it is needed in order to work properly?

In the first example you are declaring a copy of the contents of the array element in a local variable:

var item = items[i];

As the inline comment says, here we're remembering the value within the closure scope.

In the 2nd example, instead of passing i as a parameter, you could also have done:

   (function() {

       var prop = i; // See here!

       console.log("property: " + i);
       // Create a nwe getter for the property
       that["get" + prop] = function() {
           return properties[prop];
        };

        // Create a new setter  for the property
       that["set" + prop] = function(val) {
           properties[prop] = val;
       };
   })(); 

Which makes both examples more similar.

Similarly, you could also alter the first example to pass i as a parameter, rather than declaring it verbosely in a variable.

(function(item) {
    // Bind a function to the element
    obj["on" + items[item] = function() {
        // item refers to a parent variable that has been successfully
        // scoped within the context of this loop
        alert("thanks for your " + items[item]);
    };
})(i);     

It's arbitrary as to whether you declare a local copy of the variable using a var statement, or pass it as a parameter into your self executing function.

Edit:

@Zecc bought up a good point in the comments, which I'd like to explain:

(function (i) {

    // using `i` works as expected. 

}(i));

Where as:

(function () {

    var i = i;

    // using `i` doesn't work... i = undefined.

}());

This is because the var variable = value statement is effectively:

(function () {

    var i;

    i = i;

}());    

and the var keyword always assigns the variable(s) following it with the value undefined.