继承原型中的私有变量

我想我误解了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());​

这会输出1 - 2 (在JSBin上测试它),而我完全预计结果是1 - 1 ,因为我想要两个单独的对象。

如何确保A对象不是B多个实例之间的共享对象?

更新 :本文重点介绍一些问题:

问题是,每种方法用于创建私有变量的作用域都可以正常工作,这也是操作中的关闭,如果您为一个对象实例更改了私有变量,则会导致该变量的全部更改。 即它更像是一个私有静态属性,而不是一个实际的私有变量。

所以,如果你想拥有一些私人的东西,更像是一个非公共的常量,上述任何一种方法都是好的,但不适用于实际的私有变量。 私有变量只适用于JavaScript中的单例对象。

解决方案 :根据BGerrissen的回答,更改B的声明并按原样离开原型工程:

var B = function() { A.apply(this, arguments); };

私人成员使用原型继承是棘手的。 首先,它们不能被继承。 您需要在每个单独的构造函数中创建私有成员。 您可以通过在子类中应用超级构造函数或创建装饰器来实现此目的。

装饰者例子:

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

这只是超级构造函数调用的变体,你也可以通过调用实际的超级构造函数来实现.apply()

var B = function(){
    A.apply(this, arguments);
}

现在通过应用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.
}

你使用如下:

B.prototype = Object.beget(A.prototype);

当然,你可以放弃继承,并且充分利用装饰器,至少在需要私人成员的地方。


你需要忘记上课的想法。 在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()
    };
    

    我个人倾向于在大多数时候避免构造函数和原型继承。 与面向对象背景的人相比,他们在JS中的用处要少得多。

    更新我会对你在更新中引用的陈述保持警惕:

    私有变量只适用于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
    
    链接地址: http://www.djcxy.com/p/60769.html

    上一篇: Private variables in inherited prototypes

    下一篇: Use of 'prototype' vs. 'this' in JavaScript?