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.
In Method #2 there are two problems:
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];
.
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]));
}
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]());