简体   繁体   English

实现原型继承的正确方法

[英]The correct way to implement prototypal inheritance

There is already numerous threads about prototypal inheritance in JavaScript, but this is not a copy due to laziness. 在JavaScript中已经有很多关于原型继承的线程,但由于懒惰,这不是副本。 I have literally read them all and have found almost as many different syntactic approaches as I have found answers, so it appears that I am not the only one that is confused on this subject! 我已经完全阅读了所有内容,并且发现了几乎与我找到答案一样多的不同语法方法,所以看起来我并不是唯一一个对此主题感到困惑的人!

Specifics: 具体细节:

My current approach looks like this. 我目前的做法是这样的。

var Person = function(name) {
    this.name = name;

    this.greeting = function() {
        alert("Greetings, I am " + name);
    }

}

var bob = new Person("bob");
bob.greeting();

var Woman = function(name) {
    Person.call(this, name);
    this.gender = "female";
}

Woman.prototype = Object.create(Person.prototype);

var brenda = new Woman("brenda");
brenda.greeting();

Person.prototype.eats = true;

alert(brenda.eats);

Having tested the code I have found it works perfectly - as far as I can tell - but I have been told that this is not the best approach and that I should define the constructor like this: 测试过代码之后我发现它完美无缺 - 据我所知 - 但我被告知这不是最好的方法,我应该像这样定义构造函数:

Woman.prototype.constructor = Woman;

And not use the Person.call method in my actual constructor method. 而不是在我的实际构造方法中使用Person.call方法。 Two things, having been told that I can't see an easy way to then pass in the parameters using the 2nd approach, and also, just why? 有两件事,被告知我看不到一个简单的方法,然后使用第二种方法传递参数,还有,为什么呢? What I'm doing seems to work fine. 我正在做的似乎工作得很好。

Am I missing something? 我错过了什么吗?

Is there some cases where what I'm doing will give unpredictable errors? 在某些情况下,我正在做的事情会给出不可预测的错误吗?

Can anyone give a definitive 'correct' approach and the reason for it? 任何人都可以给出明确的“正确”方法及其原因吗?

What you have is, I think, considered to be the best approach. 我认为,你所拥有的是最好的方法。 The point you raise however is irrelevant: 你提出的观点是无关紧要的:

Woman.prototype.constructor = Woman;

is not a replacement for Person.call(...) . 不是Person.call(...)的替代品。 In fact, those two things have nothing in common, they serve different purposes: 事实上,这两件事没有任何共同之处,它们有不同的用途:

  • Calling the parent constructor in the child constructor ensures that the child instance is correctly initialized. 在子构造函数中调用父构造函数可确保正确初始化子实例。 It's like calling super() in ES6 or other languages. 这就像在ES6或其他语言中调用super()

  • Assigning to constructor simply restores the original value of Women.prototype.constructor . 分配给constructor只是恢复Women.prototype.constructor的原始值。 If you did not do that, brenda.constructor would refer to Person . 如果你不这样做, brenda.constructor会引用Person This doesn't have an impact on the internal workings of your inheritance, but other code that consumes your object might rely on constructor having the correct value. 这对继承的内部工作没有影响,但是使用对象的其他代码可能依赖于具有正确值的constructor See also Advantages of setting the "constructor" Property in the "prototype" . 另请参见在“原型”中设置“构造函数”属性的优点

So the answer to your question is: you should do both. 所以你的问题的答案是:你应该做到这两点。

Sometimes you see the assignment taking place as part of the Object.create call. 有时您会看到作为Object.create调用的一部分进行的赋值。 This is even more accurate, since it recreates the original characteristics of the property (like non-enumerability): 这更加准确,因为它重新创建了属性的原始特征(如非可枚举性):

Woman.prototype = Object.create(Person.prototype, {
  constructor: {value: Woman, writable: true}
});

FWIW, I believe one of the reasons of introducing class in ES6 was to reduce the confusion around constructor functions and prototypes. FWIW,我相信在ES6中引入class的原因之一是减少构造函数和原型的混淆。 That is not to say that you should not know about it, after all, class is more or less just syntactic sugar, but it just makes inheritance a bit easier: 这并不是说你不应该知道它,毕竟, class或多或少只是语法糖,但它只是使继承更容易:

class Woman extends Person { // almost like Woman.prototype = Object.create(Person.prototype)
  constructor(name) {
    super(name); // almost like Person.call, but enforced by the interpreter
    this.gender = "female";
  }
}

this is OOP approach in JS 这是JS中的OOP方法

var Person = function(name) {
    this.name = name;
}

Person.prototype.greeting = function(){ 
    var newPerson = new Person(this.name);
    alert("Greetings, I am " + name);
    return newPerson;
} 

Person.prototype.toString=function(){ 
    return '[Person "'+this.name+'"]';
} 

Woman.prototype = new Person();

Woman.prototype.constructor=Woman;      

function Woman(name){ 
    this.name=name;
} 

Woman.prototype.toString=function(){ 
    return '[Woman "'+this.name+'"]';
} 

var somePerson = new Person('Mr.');
var myPerson = new Woman('She');
alert('somePerson is '+somePerson);   // results in 'somePerson is [Person "Mr."]' 
alert('myPerson is '+myPerson);             // results in 'myPerson is [Woman "She"]' 

myPerson.greeting();                    // calls a method inherited from Person 

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

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