2012-08-06 75 views
2

方法#1Javascript關閉?

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沒有。

+1

** **唯一的新功能引入新的變量。這個問題已經來臨在SO上好一點。搜索「JavaScript循環最後值」或類似的。 – 2012-08-06 01:12:32

+1

看到[這個答案](http://stackoverflow.com/a/3903130/373378)一個很好的解釋。 – 2012-08-06 01:16:39

+1

在你的非工作代碼中,把'var a' *放在'for'循環之前,它會更好地反映現實。沒有變量是'for'語句的局部變量,因爲在JavaScript中,範圍總是由'function'定義的。這意味着'for'循環中所有正在創建的函數都指向''''變量,它是'transform'函數的本地變量。 – 2012-08-06 01:21:48

回答

4

在方法#2有兩個問題:

  1. 您正在返回鍵名,a,而不是數組值,ar[a]。那就是,而不是return a;,你需要return ar[a];

  2. 功能總是引用的最後一個值環通過,因爲它引用同一範圍內的對象。要創建一個新的作用域對象,您需要一個閉包,一個with塊或一個綁定函數。

與閉合:

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])); 
} 
+0

點#1是從我身邊的錯字,對不起。仍試圖理解第二點。 – Zaje 2012-08-06 01:16:27

+0

@Zaje我的編輯幫助嗎? – 2012-08-06 01:24:08

+0

@PeterOlson,非常感謝兄弟。編輯確實有幫助。 – Zaje 2012-08-06 01:34:14

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

這是一個比較正確的模式,由於這樣的事實,開始關閉具有非常明顯的影響。然而,使用綁定,往往是一個功能方法,特別是在函數調用時(特別是上下文或特定約束)。

使用與塊也有一些缺點(有很多的問題,這件事)。

獎金:如果你想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]()); 
+0

撇開:封閉[更快](http://jsperf.com/bind-v-closure)比'.bind' – 2012-08-06 01:46:43