我对原型继承的理解是每个对象都有一个原型属性。如果某个对象上不存在某个属性,那么它的原型对象就会被检查,依此类推,直到链上。
在这个例子中,我的原型对象是一个带有计数器属性的简单对象。
我希望每个实例共享相同的原型,但它们似乎会获得新的实例。这段代码的输出是00,我期待01。
我错过了一些明显的东西吗?
"use strict";
var ConstructorFunction = function () {};
ConstructorFunction.prototype = {
counter: 0,
count: function () {
return this.counter++;
}
};
var a = new ConstructorFunction();
var b = new ConstructorFunction();
$("#output").append(a.count());
$("#output").append(b.count());
这是jsfiddle:http://jsfiddle.net/hdA6G/5/
答案 0 :(得分:4)
原型属性确实是所有实例的共享。问题是你永远不会改变原型属性。看看你的fiddle还有一些额外的日志:
"use strict";
var ConstructorFunction = function () {};
ConstructorFunction.prototype = {
counter: 0,
count: function () {
return ++this.counter;
}
};
var a = new ConstructorFunction();
var b = new ConstructorFunction();
$("#output").append(a.hasOwnProperty("counter") + " "); //false
a.count();
$("#output").append(a.hasOwnProperty("counter") + " "); //true
如您所见,只要您调用++this.counter
,就会创建一个本地属性,该属性将从中使用。
我假设发生了这种情况:
++this.counter
被解释为this.counter = this.counter + 1
。首先,评估等号的右半部分,由于您的实例没有counter
属性,因此使用原型的计数器属性。此属性的值将被添加到1,然后分配给this.counter
,现在创建一个本地属性,就像它分配一个根本不存在的属性一样,如a.xy = 1
。在这种情况下,xy
将是实例的本地属性。
修改强>
有两种解决方法仍然允许您使用prototype属性:
1)在count
方法中明确设置prototype属性:
ConstructorFunction.prototype.count = function() {
return ++this.constructor.prototype.counter;
};
2)使用apply
调用count方法,并将原型用作上下文:
a.count.apply(a.constructor.prototype);
但是,如果按照您的方式设置prototype
属性,则会遇到这两种方法的问题。
ConstructorFunction.prototype = {
//...
};
这会覆盖完整的原型对象,因此也会覆盖它的构造函数属性。构造函数属性现在将指向原型链中的下一个更高对象Object
对象。要解决这个问题,您可以在分配原型对象后手动设置构造函数:
ConstructorFunction.prototype.constructor = ConstructorFunction;
或单独分配原型对象的每个属性:
ConstructorFunction.prototype.counter = 0;
ConstructorFunction.prototype.count = function () {
return ++this.counter;
};
答案 1 :(得分:3)
考虑使用闭包,这样您就可以将该变量保持为私有并跨实例共享:
var Class = (function ClassModule() {
var counter = 0;
function Class() {}
Class.prototype = {
count: function() {
return counter++;
}
};
return Class;
}());
var class1 = new Class();
var class2 = new Class();
console.log(class1.count()); //=> 0
console.log(class2.count()); //=> 1
counter
变量与闭包(模块模式)保持在上下文中,并且不会与每个新实例重复。
答案 2 :(得分:1)
在基于原型的OOD中,共享意味着共享相同的定义并将其用于不同的上下文。如果你需要在上下文之间共享静态属性,你可以按照下面的说明进行操作
var ConstructorFunction = function (context) {
this.count = function () {
return context + "," + (++this.counter) + "," + (++ConstructorFunction.staticCounter);
};
};
ConstructorFunction.prototype.counter = 0;
ConstructorFunction.staticCounter = 0;
var context1 = new ConstructorFunction("context1");
var context2 = new ConstructorFunction("context2");
$("#output").append(context1.count());
$("#output").append(" ");
$("#output").append(context2.count());
以及更好的定义方法
var ConstructorFunction = function (context) {
this.context = context;
this.counter = 0;
};
ConstructorFunction.staticCounter = 0;
ConstructorFunction.prototype.count = function () {
return this.context + "," + (++this.counter) + "," + (++ConstructorFunction.staticCounter);
};
答案 3 :(得分:0)
你的继承向后,原型继承自基础对象。我认为这与你想要完成的事情更相似。
function MyObject () {
this.count = 0;
};
ConstructorFunction.prototype = {
counter: function () {
return this.count++;
},
print: function () {
return this.count;
}
};
Var Foo = new MyObject();
console.log(Foo.counter()); // 1
console.log(Foo.counter()); // 2
console.log(Foo.print()); // 2
我希望这会有所帮助。
修改强>
如果您希望计数器在所有实例中共享,那么它应该放在您的基础对象中。
function MyObject () {
this.count = 0;
this.counter = function () {
return this.count++; //may not need the this
}
};
答案 4 :(得分:0)
我知道这是一个老帖子,但我是Javascrpt的新手,在尝试时遇到了类似的东西,所以想要投入2美分。
一旦使用new
实例化,每个对象都会获得自己在原型中声明的变量副本。从那时起,使用this
进行访问将进行常规查找并找到操作的私有副本。
但是,如果在创建所有对象后创建原型变量,则该变量将被共享并且行为类似于静态。我认为解释为什么会发生这种情况相当简单,但是,尽管如此,我发现这是一个有趣的黑客。我不确定这是否是技术规范中可能在未来版本中解决的错误,或标准行为的副作用,因此不知道这是否可靠。我甚至不确定这是否是一个新推出的功能'在该语言的更高版本中。实际上,我开始在谷歌上搜索这个事实,并发现了这篇文章。
试试这段代码。
var Worker = function (name) {
this.name = name;
}
Worker.prototype.jobs = 0;
Worker.prototype.Work = function () {
console.log("jobs finished", this.name, ++this.jobs);
}
Worker.prototype.WorkCount = function () {
console.log("work count", this.name, this.workCount);
}
var ca = new Worker("ca");
var cb = new Worker("cb");
ca.Work();// shows 1
cb.Work();// shows 1
ca.WorkCount();// shows undefined
cb.WorkCount();// shows undefined
Worker.prototype.workCount = 2;
ca.WorkCount();// shows 2
cb.WorkCount();// shows 2