Javascript关闭?

时间:2012-08-06 01:07:33

标签: javascript closures

方法#1

function transform(ar) {
    var alStr = [];
    for(var i=0; i<ar.length; i++) {

        alStr[i] = (function(v) {
            return (function() {
                return v;
            });
        }(ar[i]));
    }

    return alStr;
}

var a = ["a", 24, { foo: "bar" }];
var b = transform(a);
a[1];
b[1]();

方法#2

function transform(ar) {
    var alStr = [];
    for(var a in ar) {
        var O = function() {
            return a;
        }
        alStr.push(O);
    }
    return alStr; 
}

var a = ["a", 24, { foo: "bar" }];
var b = transform(a);
a[1];
b[1]();

上述方法用于将数组对象转换为单个函数,这些函数在执行时返回特定的数组对象。想知道为什么方法#1工作,方法#2不工作。

2 个答案:

答案 0 :(得分:4)

在方法#2中有两个问题:

  1. 您将返回键名a,而不是数组值ar[a]。也就是说,而不是return a;你想要的return ar[a];

  2. 该函数将始终引用循环的最后一个值,因为它引用相同的范围对象。要创建新的范围对象,您需要一个闭包,with块或绑定函数。

  3. 关闭:

    for(var a in ar) { 
      var O = (function(val) { 
        return function() { 
          return val; 
         }
      })(ar[a]);
      alStr.push(O); 
    }
    

    使用with块:

    for(var a in ar) { 
      with({val: ar[a]}) {
        alStr.push(function() { 
          return val; 
         });
      }
    } 
    

    使用绑定功能:

    for(var a in ar) { 
      var O = function(x) { return x; };
      alStr.push(O.bind(null, arr[a]));
    } 
    

答案 1 :(得分:3)

彼得奥尔森是对的。但是,更现代(正确?)的方法是使用function.bind(obj, val)。最近引入,function.bind允许您按值和在某些上下文中传递变量。阅读更多here

所以,你可以这样写:

function transform(ar) {
    var alStr = [];
    var O = function(x) { return x }
    for(var a in ar) {
        alStr.push(O.bind(null, ar[a]));
    }
    return alStr; 
}

var a = ["a", 24, 12345];
var b = transform(a);
console.log(a[2]);
b[2]();

这是一个更正确的范例,因为启动闭包具有非常明确的含义。但是,使用bind往往是一种在函数调用时特别使用的函数式方法(特别是在上下文或特定的规定中)。

使用with块也有一些缺点(有很多问题)。

奖励:如果您希望b也代表对a数组的后续更改,则此解决方案可以解决该问题:

function transform(ar) {
    var alStr = [];
    var O = function(x) { return ar[x] }
    for(var a in ar) {
        alStr.push(O.bind(null, a));
    }
    return alStr; 
}

var a = ["a", 24, 12345];
var b = transform(a);
console.log(a[2]);
console.log(b[2]());
console.log("*********");
a[2] = "new value!";
console.log(a[2]);
console.log(b[2]());