在MDN文章《面向对象Javascript介绍》中关于继承的部分,我注意到他们设置了prototype.constructor:
// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;
这有什么重要的目的吗?可以省略吗?
在MDN文章《面向对象Javascript介绍》中关于继承的部分,我注意到他们设置了prototype.constructor:
// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;
这有什么重要的目的吗?可以省略吗?
当前回答
它并不总是必要的,但它确实有它的用途。假设我们想在基类Person上创建一个复制方法。是这样的:
// define the Person Class
function Person(name) {
this.name = name;
}
Person.prototype.copy = function() {
// return new Person(this.name); // just as bad
return new this.constructor(this.name);
};
// define the Student class
function Student(name) {
Person.call(this, name);
}
// inherit Person
Student.prototype = Object.create(Person.prototype);
现在,当我们创建一个新的Student并复制它时会发生什么?
var student1 = new Student("trinth");
console.log(student1.copy() instanceof Student); // => false
副本不是Student的实例。这是因为(如果没有显式检查),我们将无法从“基”类返回Student副本。我们只能返回Person。然而,如果我们重置了构造函数:
// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;
...然后一切都按照预期进行:
var student1 = new Student("trinth");
console.log(student1.copy() instanceof Student); // => true
其他回答
它并不总是必要的,但它确实有它的用途。假设我们想在基类Person上创建一个复制方法。是这样的:
// define the Person Class
function Person(name) {
this.name = name;
}
Person.prototype.copy = function() {
// return new Person(this.name); // just as bad
return new this.constructor(this.name);
};
// define the Student class
function Student(name) {
Person.call(this, name);
}
// inherit Person
Student.prototype = Object.create(Person.prototype);
现在,当我们创建一个新的Student并复制它时会发生什么?
var student1 = new Student("trinth");
console.log(student1.copy() instanceof Student); // => false
副本不是Student的实例。这是因为(如果没有显式检查),我们将无法从“基”类返回Student副本。我们只能返回Person。然而,如果我们重置了构造函数:
// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;
...然后一切都按照预期进行:
var student1 = new Student("trinth");
console.log(student1.copy() instanceof Student); // => true
编辑,其实我错了。注释掉一行并不会改变它的行为。(我测试过)
是的,这是必要的。当你这样做时
Student.prototype = new Person();
student。prototype。constructor变成Person。因此,调用Student()将返回Person创建的对象。如果你这样做了
Student.prototype.constructor = Student;
Student.prototype.constructor被重置回Student。现在,当您调用Student()时,它执行Student,它调用父构造函数parent(),它返回正确继承的对象。如果你在调用Student.prototype.constructor之前没有重置它,你将得到一个没有在Student()中设置的任何属性的对象。
到目前为止,困惑仍然存在。
按照原来的例子,你有一个现有的对象student1 as:
var student1 = new Student("Janet", "Applied Physics");
假设你不想知道student1是如何创建的,你只是想要另一个类似的对象,你可以使用student1的构造函数属性:
var student2 = new student1.constructor("Mark", "Object-Oriented JavaScript");
在这里,如果没有设置构造函数属性,它将无法从Student获取属性。相反,它将创建一个Person对象。
这有什么重要的目的吗?
是也不是。
在ES5和更早的版本中,JavaScript本身并不使用构造函数。它定义了函数原型属性的默认对象将拥有它,并且它将引用回函数,就是这样。规范中没有其他内容提到它。
这种情况在ES2015 (ES6)中改变了,它开始在继承层次结构中使用它。例如,Promise#然后在构建要返回的新Promise时使用调用它的Promise的构造函数属性(通过SpeciesConstructor)。它还涉及数组的子类型(通过ArraySpeciesCreate)。
在语言本身之外,有时人们会在试图构建通用的“克隆”函数时使用它,或者只是在他们想要引用他们认为是对象的构造函数时使用它。我的经验是,很少有人使用它,但有时确实有人使用它。
可以省略吗?
它默认存在,你只需要在替换一个函数的prototype属性的对象时把它放回去:
Student.prototype = Object.create(Person.prototype);
如果你不这样做:
Student.prototype.constructor = Student;
...那么Student.prototype.constructor继承自Person。原型(假设)有构造函数= Person。所以这是误导。当然,如果你要子类化一些使用它的东西(比如Promise或Array),而不是使用类¹(它为你处理这个),你会想要确保你正确地设置它。所以基本上,这是个好主意。
如果您的代码(或您使用的库代码)中没有使用它,那也没关系。我一直确保它是正确连接的。
当然,对于ES2015(又名ES6)的class关键字,大多数时候我们会使用它,我们不再需要它了,因为当我们使用它时它已经为我们处理了
class Student extends Person {
}
¹”…如果你正在子类化一些使用它的东西(比如Promise或Array),而不使用class……”-这是可以做到的,但这真的很痛苦(而且有点傻)。你必须使用reflect。construct。
这有一个巨大的陷阱,如果你写
Student.prototype.constructor = Student;
但如果有一个老师,他的原型也是人,你写
Teacher.prototype.constructor = Teacher;
那么学生构造函数现在是老师!
编辑: 您可以通过确保您已经使用使用Object创建的Person类的新实例设置了Student和Teacher原型来避免这种情况。创建,如Mozilla示例中所示。
Student.prototype = Object.create(Person.prototype);
Teacher.prototype = Object.create(Person.prototype);