为什么在分配DerivedClass.prototype时使用Object.create而不是new Super

Kar*_*and 2 javascript inheritance

我一直在阅读使用Javascript的面向对象编程的MDN指南,其中在一个示例中显示继承,提到以下内容:

// Create a Student.prototype object that inherits from Person.prototype.
// Note: A common error here is to use "new Person()" to create the
// Student.prototype. That's incorrect for several reasons, not least 
// that we don't have anything to give Person for the "firstName" 
// argument. The correct place to call Person is above, where we call 
// it from Student.
Student.prototype = Object.create(Person.prototype); // See note below
Run Code Online (Sandbox Code Playgroud)

我也研究了其他答案,但他们没有具体说明使用时会遇到什么问题 Student.prototype = new Person()

上面提到的一个问题涉及传球firstName,但这只是一个场景.

在任何人将此标记为重复之前.这里的问题使用"Object.create"而不是"new"处理通常使用Object.create而不是构造函数,而不是我所要求的.

这里的问题是什么是使用在Derived.prototype =新基地"新"的关键字的原因涉及区别foo()new foo()

这个问题的一个答案JavaScript继承:Object.create vs new给出了另一个原因:

当SomeBaseClass有一个函数体时,这将使用new关键字执行.这通常不是意图 - 您只想设置原型链.在某些情况下,它甚至可能导致严重问题,因为您实际实例化了一个对象,其私有范围变量由所有MyClass实例共享,因为它们继承了相同的特权方法.其他副作用是可以想象的.

所以,你通常应该更喜欢Object.create.然而,某些传统浏览器不支持它; 这就是你认为新方法过于频繁的原因,因为它往往没有(明显的)伤害.还看看这个答案.

阅读上述内容似乎更多地取决于所处理的特定情景.是否有严格的理由让MDN说使用new SuperClass()不正确?

T.J*_*der 6

TL; DR可以使用它new Person来构建您的Student.prototype,但这样做会使您的所有构造函数复杂化,限制它们检测构造错误的能力,并要求所有构造函数都处理这种使用形式.由于它更复杂,容易出错和限制,大多数人都采取了另一种方式:Object.create.

主要的实际问题是:如果超级构造函数需要参数怎么办?说Person需要一个名字:

function Person(name) {
    this.name = name;
}
Run Code Online (Sandbox Code Playgroud)

我如何new Person用来创建我的原型?我没有name提供它.

现在,有两种方法可以解决这个问题:

  1. 要求所有需要参数的构造函数检测到它们是在没有参数的情况下调用的,假设它们被调用来构建原型,并正确处理该情况.

  2. Object.create改用.

这是第一种方式:

// NOT WHAT MOST PEOPLE DO
function Person(name) {
    if (arguments.length === 0) {
        return;
    }
    this.name = name;
}
function Student(name) {
    Person.call(this, name);
}
Student.prototype = new Person;
Student.prototype.constructor = Student;
Run Code Online (Sandbox Code Playgroud)

注意如何Person检测它没有参数被调用,希望这是因为它被用于创建原型而不是出于其他原因,并避免尝试使用缺少的参数.

这容易出错且很尴尬,这意味着Person如果没有收到name最终实例构造调用,则不能(例如)引发错误.它只是不知道为什么它被调用,所以它无法判断是否name正确.

所以大多数人走的是另一种方式:

// What most people do
function Person(name) {
    this.name = name;
}
function Student(name) {
    Person.call(this, name);
}
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
Run Code Online (Sandbox Code Playgroud)

Person它不必包含任何特殊情况代码,并且如果它没有得到name并且需要它,则可以自由地引发错误.

现在,这也是根深蒂固的,它已被纳入ES2015(又名ES6)的class关键词.这个ES2015代码:

class Student extends Person {
    constructor(name) {
        super(name);
    }
}
Run Code Online (Sandbox Code Playgroud)

翻译与Object.create上面的ES5代码非常接近.(不完全,但非常密切.)