我想我误解了Javascript原型继承是如何工作的.具体来说,原型内部变量似乎在多个不同的子对象之间共享.用代码说明最容易:
var A = function()
{
var internal = 0;
this.increment = function()
{
return ++internal;
};
};
var B = function() {};
// inherit from A
B.prototype = new A;
x = new B;
y = new B;
$('#hello').text(x.increment() + " - " + y.increment());?
Run Code Online (Sandbox Code Playgroud)
这输出1 - 2(在JSBin上测试),而我完全期望结果1 - 1,因为我想要两个单独的对象.
如何确保A对象不是多个实例之间的共享对象B?
更新:本文重点介绍了一些问题:
问题是每个方法用于创建一个工作正常的私有变量的范围,也就是操作中的闭包,如果您更改一个对象实例的私有变量,它将被更改为all.即它更像是私有静态属性,而不是实际的私有变量.
所以,如果你想拥有私有的东西,更像是非公共常量,上述任何一种方法都是好的,但不适用于实际的私有变量.私有变量仅适用于JavaScript中的单例对象.
解决方案:根据BGerrissen的回答,更改B原型的声明和离开按预期工作:
var B = function() { A.apply(this, arguments); };
Run Code Online (Sandbox Code Playgroud)
BGe*_*sen 17
私人成员使用原型继承是棘手的.首先,它们不能被继承.您需要在每个单独的构造函数中创建私有成员.您可以通过在子类中应用超级构造函数或创建装饰器来完成此操作.
装饰者的例子:
function internalDecorator(obj){
var internal = 0;
obj.increment = function(){
return ++internal;
}
}
var A = function(){
internalDecorator(this);
}
A.prototype = {public:function(){/*etc*/}}
var B = function(){
internalDecorator(this);
}
B.prototype = new A(); // inherits 'public' but ALSO redundant private member code.
var a = new B(); // has it's own private members
var b = new B(); // has it's own private members
Run Code Online (Sandbox Code Playgroud)
这只是超级构造函数调用的变体,您也可以通过调用实际的超级构造函数来实现相同的 .apply()
var B = function(){
A.apply(this, arguments);
}
Run Code Online (Sandbox Code Playgroud)
现在通过B.prototype = new A()您应用继承来调用不必要的构造函数代码A.避免这种情况的一种方法是使用Douglas Crockfords beget方法:
Object.beget = function(obj){
var fn = function(){}
fn.prototype = obj;
return new fn(); // now only its prototype is cloned.
}
Run Code Online (Sandbox Code Playgroud)
你使用如下:
B.prototype = Object.beget(A.prototype);
Run Code Online (Sandbox Code Playgroud)
当然,你可以完全放弃继承并充分利用装饰器,至少在需要私人成员的地方.
Dan*_*ker 16
你需要忘记课程的想法.在JavaScript中并不存在像'B实例'这样的东西.只有'通过调用构造函数B'获得了一些对象.对象具有属性.有些是它的"自有"属性,其他属性通过搜索原型链包含在内.
当你说new A,你正在创建一个对象.然后将其指定为B的原型,这意味着每次调用都会new B生成一个具有相同直接原型的新对象,因此具有相同的计数器变量.
在Tim Down的回答中,显示了两种选择.他incrementPublic使用继承,但使计数器变量公开(即放弃封装).而incrementInternal使得私人柜台,而是由代码移入成功B(即放弃继承).
你想要的是三件事的组合:
this.问题是最后两个之间的矛盾.我还要说继承在JS中的价值有限.最好将其视为一种功能语言:
// higher-order function, returns another function with counter state
var makeCounter = function() {
var c = 0;
return function() { return ++c; };
};
// make an object with an 'increment' method:
var incrementable = {
increment: makeCounter()
};
Run Code Online (Sandbox Code Playgroud)
就个人而言,我倾向于在大多数时候避免使用构造函数和原型继承.它们在JS中的用处远远少于OO背景的人所假设的.
更新我对您在更新中引用的声明持谨慎态度:
私有变量仅适用于JavaScript中的单例对象.
那不是真的.对象只是属性的"字典",其中任何一个都可以是函数.所以忘记对象并考虑函数.您可以通过编写返回函数的函数,根据某种模式创建函数的多个实例.这个makeCounter例子只是一个简单的例子.makeCounter不是"单例对象",并且不必限于在单例对象中使用.
原型的要点是它在多个对象(即由相同构造函数创建的对象)之间共享.如果需要在共享原型的对象之间不共享的变量,则需要将这些变量保留在每个对象的构造函数中.只需使用原型来共享方法.
在您的示例中,您无法使用原型完全执行您想要的操作.这是你可以做的事情.请参阅Daniel Earwicker的答案以获得更多解释,即我现在在这里复制是没有意义的.
var A = function() {};
A.prototype.incrementPublic = function()
{
return ++this.publicProperty;
};
var B = function()
{
this.publicProperty = 0;
var internal = 0;
this.incrementInternal = function()
{
return ++internal;
};
};
B.prototype = new A();
var x = new B(), y = new B();
console.log(x.incrementPublic(), y.incrementPublic()); // 1, 1
console.log(x.incrementInternal(), y.incrementInternal()); // 1, 1
Run Code Online (Sandbox Code Playgroud)