继承原型中的私有变量

Veg*_*sen 20 javascript

我想我误解了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(即放弃继承).

你想要的是三件事的组合:

  • 可继承行为 - 所以必须在A中定义并且除了设置原型之外不需要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不是"单例对象",并且不必限于在单例对象中使用.


Tim*_*own 5

原型的要点是它在多个对象(即由相同构造函数创建的对象)之间共享.如果需要在共享原型的对象之间不共享的变量,则需要将这些变量保留在每个对象的构造函数中.只需使用原型来共享方法.

在您的示例中,您无法使用原型完全执行您想要的操作.这是你可以做的事情.请参阅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)