简体   繁体   English

JavaScript原型继承解决方法

[英]JavaScript prototypal inheritance workaround

Here is an example: 这是一个例子:

var Box = function() {
    this.id = generateUniqueId();
};
Box.prototype = {
    add:function(parent) {
        parent.appendChild(this.elm);
    }
};

var NewBox = function() {
    this.newThing = true;
};
NewBox.prototype = new Box();
NewBox.prototype.remove = function() {
    this.elm.parentNode.removeChild(this.elm);
};

var a = new NewBox();
var b = new NewBox();
alert(a.id); // alerts 0
alert(b.id); // also alerts 0! :@

I would like to have a and b (basically each time I create a NewBox ) have their own id . 我希望a和b(基本上每次创建NewBox )都有自己的id I understand that NewBox is using prototypal inheritance and is thus inheriting from a single instance of the generated id that it gets from Box , but I would like it so that each NewBox gets its own id without having to explicitly say that inside the NewBox constructor. 我了解到NewBox使用的是原型继承,因此是从Box生成的id的单个实例继承的,但是我希望这样做,以便每个NewBox都有自己的id而不必在NewBox构造函数中明确NewBox

Maybe it's impossible or I'm doing something really wrong, so please help! 也许这是不可能的,或者我做错了什么,所以请帮助!

Thanks a lot! 非常感谢!

In your example, the Box constructor gets executed only when you set the NewBox.prototype object. 在您的示例中,仅当您设置NewBox.prototype对象时,才执行Box构造函数。

You could workaround this by calling the Box constructor function inside NewBox with the Function.prototype.apply method, to set the this value and forward all the argument values, for example: 您可以通过使用Function.prototype.apply方法在NewBox调用Box构造函数来解决此问题,以设置this值并转发所有参数值,例如:

//..
var NewBox = function() {
    Box.apply(this, arguments);
    this.newThing = true;
};
//..

var a = new NewBox();
var b = new NewBox();

// assuming your `generateUniqueId` function
// increments a number

alert(a.id); // will alert 1
alert(b.id); // will alert 2

Now, each time the NewBox constructor is called to create a new object instance ( new NewBox(); ), it will call the Box function to apply all its logic on it. 现在,每次调用NewBox构造函数创建一个新的对象实例( new NewBox(); )时,它将调用Box函数在其上应用其所有逻辑。 This will help you to avoid repeating the logic of the parent constructor over and over. 这将帮助您避免一遍又一遍地重复父构造函数的逻辑。

The apply , is used call the "parent" constructor function setting the this value to the object instance that is being created by the NewBox constructor and we pass all arguments provided to this function. 使用apply调用“父”构造函数, this值设置为由NewBox构造函数创建的对象实例,然后将提供给该函数的所有参数传递给该实例。

Your example also shows a common problem, when you express inheritance relationship through NewBox.prototype = new Box(); 当您通过NewBox.prototype = new Box();表示继承关系时,您的示例还显示了一个常见问题NewBox.prototype = new Box(); the Box constructor gets called and it has side effects, this new object will be initialized and your generateUniqueId function will be executed for the first time, if you want to avoid that, you need to either use a temp constructor, just to make a new object that inherits from Box.prototype , or use the new ECMAScript 5 Object.create method for the same purpose, for example: Box构造函数被调用且具有副作用,此新对象将被初始化,并且您的generateUniqueId函数将首次执行,如果要避免这种情况,您需要使用temp构造函数,只是为了创建一个新的继承自Box.prototype对象,或出于相同目的使用新的ECMAScript 5 Object.create方法,例如:

function inherit(o) {
  function Tmp() {}
  Tmp.prototype = o;
  return new Tmp();
}

//.....
NewBox.prototype = inherit(Box.prototype);

Or: 要么:

NewBox.prototype = Object.create(Box.prototype);

In that way, you express the same inheritance hierarchy without running your Box constructor that first time, avoiding any side effect that it might cause. 这样,您无需第一次运行Box构造函数即可表示相同的继承层次结构,从而避免了可能引起的任何副作用。

At last but not least, whenever you replace a function's prototype property is always recommended to restore the constructor property of this new prototype object, otherwise it will point to the wrong function. 最后但并非最不重要的一点是,始终建议您每次替换函数的prototype属性时,都要还原此新原型对象的constructor属性,否则它将指向错误的函数。

In your example, since you replace the NewBox.prototype with a new instance of Box , the NewBox.prototype.constructor property will point to Box , (your instances are affected, eg a.constructor === Box; // true ) instead of to NewBox as you would expect, we need to set it back, eg: 在您的示例中,由于您将NewBox.prototype替换为Box的新实例,因此NewBox.prototype.constructor属性将指向Box ,(您的实例受到影响,例如a.constructor === Box; // true )如您NewBox ,将其设置为NewBox ,我们需要将其重新设置,例如:

NewBox.prototype = someObject; // as any of the examples above
NewBox.prototype.constructor = NewBox;

You could abstract those details into a function, as I did in the inherit function above: 您可以将这些细节抽象为一个函数,就像我在上面的inherit函数中所做的那样:

function inherits(child, parent) {
  var obj, Tmp = function () {};
  Tmp.prototype = parent.prototype;
  obj = new Tmp();
  child.prototype = obj;
  child.prototype.constructor = child;
}

//...

// instead of setting the `NewBox.prototype` manually
inherits(NewBox, Box); // "NewBox inherits from Box"

//...

Maybe it's impossible or I'm doing something really wrong, so please help! 也许这是不可能的,或者我做错了什么,所以请帮助!

You're doing something wrong. 您做错了。

If you want instance-specific values, initialize them in the constructor, not the prototype. 如果需要特定于实例的值,请在构造函数而不是原型中初始化它们。

Matt Ball has the right idea. 马特·鲍尔(Matt Ball)有正确的想法。 Instead try: 而是尝试:

var Box = (function(){ 
    var numberOfBoxes = 0;
    function() {
        this.id = numberOfBoxes++;
    }
})();

Or in the case you want all your (different) classes to have unique ids: 或者,如果您希望所有(不同的)类具有唯一的ID:

var generateUniqueID = (function(){
    var runningCount = 0;
    return function (){
        return runningCount++;
    }
})();

var Box = function() {
    this.id = generateUniqueId();
};

var NewBox = function() {
    this.id = generateUniqueId();
    this.newThing = true;
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM