我正在尝试解决这个有困难的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
为什么会这样?
答案 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中所做的事情。问题是,当你开始解雇时,我是最后一件事,'伽玛'。当您将引用作为参数传递给函数时,该函数会在传递时保持值的状态。