Javascript类和变量引用

时间:2012-04-02 21:29:42

标签: javascript

我正在尝试解决这个有困难的Javascript OOP问题。

所以我有以下课程:

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

为什么会这样?

4 个答案:

答案 0 :(得分:6)

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

你的问题就在这里。当此循环结束时,i是最后一个元素。每个函数使用相同的i,因此它们都是最后一个元素。

当您使用addMethod时,您正在关闭以“捕获”正确的值。

编辑:当您调用addMethod时,您正在“复制”该值,而不是使用i值,该值随每次循环迭代而变化。

答案 1 :(得分: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" 1}},所以无论你调用哪种方法,都是i的值,他们都会登录到控制台。 JS在创建方法时不存储i的当前值。

JS为每个函数创建一个“闭包” - 在initialize函数中声明的变量(即i)继续在嵌套函数的范围内,即使在{ {1}}已完成

第二个版本调用initialize添加每个方法:

addMethod

...所以当它们运行时,它们将引用它们自己的addMethod: function(method) { this[method] = function() { console.log(method); } } 参数的“副本”,因为每个方法都有一个单独的闭包。

编辑:另请参阅此问题:How do JavaScript closures work?(有几个答案比我更清楚地解释了这一点。)

答案 2 :(得分: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"!
}

答案 3 :(得分:0)

好吧,首先停止在Arrays上使用for(属性对象)循环。这是有趣和游戏,直到有人原型到Array对象,这是一个非常合理,非常有用/受欢迎的事情。这将导致自定义方法被添加到数组循环中的for x。

至于问题,它正在完成你在版本1中所做的事情。问题是,当你开始解雇时,我是最后一件事,'伽玛'。当您将引用作为参数传递给函数时,该函数会在传递时保持值的状态。