Javascript closure?

2019-01-20 03:31发布

Method # 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]();

Method # 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]();

The above mentioned methods are used to convert an array objects into individual functions which on execution return the specific array object. Want to know why method #1 works and method #2 doesnt.

2条回答
老娘就宠你
2楼-- · 2019-01-20 04:07

In Method #2 there are two problems:

  1. You are returning the key name, a, rather than the array value, ar[a]. That is, rather than return a; you want return ar[a];.

  2. The function will always refer to the last value looped through because it references the same scope object. To create a new scope object you will need a closure, a with block, or a bound function.

With a closure:

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

With a with block:

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

With a bound function:

for(var a in ar) { 
  var O = function(x) { return x; };
  alStr.push(O.bind(null, arr[a]));
} 
查看更多
贼婆χ
3楼-- · 2019-01-20 04:12

Peter Olson is correct. However, the more modern (correct?) way of doing this is by using function.bind(obj, val). Introduced somewhat recently, function.bind allows you to pass variables by value and in certain contexts. Read more here.

So, you could write something like this:

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]();

This is a more correct paradigm due to the fact that initiating closures has very clear implications. Using bind, however, tends to be a functional approach to be used specifically when function calling (in particular contexts or with particular stipulations).

Using a with block also has some downsides (there are plenty of questions about it).

Bonus: If you wanted b to also represent subsequent changes to the a array, this solution solves that problem:

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]());
查看更多
登录 后发表回答