【问题标题】:The correct way to implement prototypal inheritance实现原型继承的正确方法
【发布时间】:2015-08-05 08:41:08
【问题描述】:

已经有很多关于 JavaScript 中原型继承的线程,但是由于懒惰,这不是一个副本。我从字面上阅读了所有这些,并且发现了几乎与我找到答案一样多的不同句法方法,所以看来我不是唯一一个对这个主题感到困惑的人!

具体说明:

我目前的做法是这样的。

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);

测试了代码后,我发现它运行良好——据我所知——但有人告诉我这不是最好的方法,我应该像这样定义构造函数:

Woman.prototype.constructor = Woman;

并且不要在我的实际构造方法中使用 Person.call 方法。有两件事,被告知我看不到使用第二种方法传递参数的简单方法,还有,为什么?我正在做的似乎工作正常。

我错过了什么吗?

在某些情况下,我正在做的事情会产生不可预知的错误吗?

谁能给出一个明确的“正确”方法及其原因?

【问题讨论】:

  • prototype.call?你的意思是Person.call
  • 我认为......我在这里可能错了,调用方法属于 Object.prototype 并且被继承。所以 Person.call 是 Person.prototype.call() 的简写......所以这就是我要说的。
  • 仅供参考,Person.callPerson.prototype.call 不同。 Person.protoype 不是函数,不能调用。
  • 最后一点:greeting 应该真正定义为Person.prototype.greeting,而不是在构造函数中。否则,您将无法利用原型(在实例之间共享数据)。

标签: javascript oop inheritance prototype


【解决方案1】:

我认为,你所拥有的被认为是最好的方法。但是,您提出的观点无关紧要:

Woman.prototype.constructor = Woman;

不能替代Person.call(...)。事实上,这两件事没有任何共同之处,它们的用途不同:

  • 在子构造函数中调用父构造函数可确保正确初始化子实例。这就像在 ES6 或其他语言中调用 super()

  • 分配给constructor 只会恢复Women.prototype.constructor 的原始值。如果您不这样做,brenda.constructor 将引用Person。这不会对继承的内部工作产生影响,但是使用您的对象的其他代码可能依赖于 constructor 具有正确的值。另见Advantages of setting the "constructor" Property in the "prototype"

所以你的问题的答案是:你应该两者都做。

有时您会在Object.create 调用中看到分配发生的情况。这更加准确,因为它重新创建了属性的原始特征(如不可枚举性):

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

FWIW,我相信在 ES6 中引入class 的原因之一是为了减少对构造函数和原型的混淆。这并不是说你不应该知道,毕竟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";
  }
}

【讨论】:

  • Felix,我毫不怀疑您是正确的,但是为了完整起见,您能否解释一下为什么我需要明确定义构造函数?对我来说这似乎很奇怪,因为很明显,当我在 woman = new Woman() 中使用 new 运算符时,它是在说 Woman 是一个构造函数。那么这对于从 Woman 类继承的对象来说是必要的吗?
  • 谢谢菲利克斯。另一个问题。根据下面的答案,许多消息来源建议不是 Woman.prototype = Object.create(Person.protype) - 我理解为制作相关原型的深层副本 - 他们说我应该简单地写 Woman.prototype = new Person() ...这实际上是一回事吗?
  • 它可以是同一件事,但它在概念上是错误的。在这里查看我的答案:Benefits of using Object.create for inheritance
  • 另外,Object.create 不进行深度复制。它创建一个以特定对象为原型的新对象。然而,原型并未被复制。这很重要,因为您对原型所做的更改会影响对象(就像您在上面对eats 所做的那样)。也许这就是你的意思,我只是想澄清一下:)
  • writable: false 也许?为什么我们希望别人改变它? ;-)
【解决方案2】:

这是 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 

【讨论】:

  • 这不是一个好方法。与Object.create相比,它有不少缺点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-19
  • 1970-01-01
  • 1970-01-01
  • 2016-09-23
  • 2012-04-11
  • 1970-01-01
相关资源
最近更新 更多