2012-04-02 59 views
6

我正試圖解決這個謎題頭腦Javascript OOP問題。Javascript類和變量引用

所以我有以下類:

var ClassA = function() { 
    this.initialize(); 
} 

ClassA.prototype = { 

    methods : ['alpha','beta','gama'], 

    initialize : function() { 
     for (var i in this.methods) { 
      this[this.methods[i]] = function() { 
       console.log(this.methods[i]); 
      } 
     } 
    } 
} 

var a = new ClassA(); 

當我打電話每一個方法我希望打印的名字吧,對不對?但這裏是我所得到的:

a.alpha(); // returns gama ?!? 
a.beta(); // returns gama ?!? 
a.gama(); // returns gama 

但是,當我的課是這樣的:

var ClassB = function() { 
    this.initialize(); 
} 

ClassB.prototype = { 

    methods : ['alpha', 'beta', 'gama'], 

    initialize: function() { 
     for (var i in this.methods) { 
      this.addMethod(this.methods[i]); 
     } 
    }, 

    addMethod: function(method) { 
     this[method] = function() { 
      console.log(method); 
     } 
    } 

} 

var b = new ClassB(); 

b.alpha(); // returns alpha 
b.beta(); // returns beta 
b.gama(); // returns gama 

這究竟是爲什麼?

+0

是不是通過在JS中的數組循環的[不正確的方式](http://stackoverflow.com/questions/3010840/loop-through-array-in-javascript#answer-3010848)? – PeeHaa 2012-04-02 21:36:48

+0

@RepWhoringPeeHaa - 是的,應該使用plain for循環,但這不是問題。 – nnnnnn 2012-04-02 21:48:47

+0

@nnnnnn我知道這就是爲什麼它是一個評論,而不是一個答案:-) – PeeHaa 2012-04-02 21:51:10

回答

6
for (var i in this.methods) { 
     this[this.methods[i]] = function() { 
      console.log(this.methods[i]); 
     } 
} 

你的問題在於此。當這個循環結束時,i是最後一個元素。每個功能使用相同的i,所以它們都是最後一個元素。

當您使用addMethod時,您正在關閉以「捕捉」正確的值。

編輯:當你呼叫addMethod你是「複製」的價值,而不是使用i價值,每個循環迭代改變。

+0

我幾乎每天都回答這樣的問題... http://stackoverflow.com/questions/9980209/register-onclick-events-from -dynamically-created-div-array-rails-jquery/9980579#9980579 – 2012-04-02 21:36:14

+0

我很困惑...不是console.log只是另一個函數,而addMethod我只是將它包裹到另一個函數中? – drinchev 2012-04-02 21:38:52

+0

@drinchev:'console.log'在這裏並不重要。這裏重要的是,當你調用'addMethod'時,你將值複製到每個函數中,但是當你沒有它時,你使用相同的值。 (對不起,如果我沒有解釋得很好。) – 2012-04-02 21:40:34

3

在你的第一個版本:

initialize : function() { 
    for (var i in this.methods) { 
     this[this.methods[i]] = function() { 
      console.log(this.methods[i]); 
     } 
    } 
} 

你內initialize所有創建的方法指的是同一i變量從initialize - 後initialize運行i具有價值"gama",所以無論哪個的方法你稱之爲i他們將登錄到控制檯的價值。 JS在創建方法時不存儲當前值i

JS創建了一個「封閉」爲每個功能 - 在您的initialize函數聲明的變量(即i)繼續在範圍爲嵌套函數(S)甚至initialize後完成

第二個版本稱addMethod添加的每個方法:

addMethod: function(method) { 
    this[method] = function() { 
     console.log(method); 
    } 
} 

...等等,當他們跑他們會參考method參數的自己的「副本」,因爲再有一個獨立的封閉爲每個方法。

編輯:另請參閱此問題:How do JavaScript closures work?(有幾個答案解釋這比我更清楚)。

1

您可以通過添加一個匿名閉包解決您的第一個例子:

initialize : function() { 
    for (var i in this.methods) { 
     (function (i) { // anonymous closure 
      this[this.methods[i]] = function() { 
       console.log(this.methods[i]); 
      } 
     }).call(this, i); // use .call() if you need "this" inside 
    } 
} 

現在,它的工作方式是你的第二個例子一樣。 「匿名」意味着閉包由沒有名字的函數構成,並且在「創建」時立即被調用。

注意側身:使用.call(this, ...)保存this調用函數裏面,或者你可以做var that = this,使用that代替this,通常調用該函數:

for (var i in this.methods) { 
    var that = this; 
    (function (i) { // anonymous closure 
     that[that.methods[i]] = function() { 
      console.log(that.methods[i]); 
     } 
    })(i); // Called normally so use "that" instead of "this"! 
} 
+1

他已經解決了這個問題,他想知道爲什麼這是一個問題。 – 2012-04-02 21:41:21

+0

是的,但這種修復方法更直接,更簡單。 – TMS 2012-04-02 21:48:18

+0

我同意,但它仍然不回答原來的問題。 – 2012-04-02 21:50:08

0

嗯,首先停止使用(對象中的屬性)在數組上循環。這是所有的樂趣和遊戲,直到有人對Array對象進行原型化,這是一個完全合理且非常有用/流行的事情。這將導致自定義方法被添加到您的數組循環中。

至於這個問題,它正在做你剛纔告訴它在版本1中做的事情。問題是,當你開始發射它時,我是最後一件事,'伽瑪'。當你將一個引用作爲參數傳遞給一個函數時,該函數在傳遞時保持該值的狀態。