2012-04-02 61 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中做的事情。问题是,当你开始发射它时,我是最后一件事,'伽玛'。当你将一个引用作为参数传递给一个函数时,该函数在传递时保持该值的状态。