在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;
这有什么重要的目的吗?可以省略吗?
当前回答
得到了一个很好的代码示例,为什么真的有必要设置原型构造函数..
function CarFactory(name){
this.name=name;
}
CarFactory.prototype.CreateNewCar = function(){
return new this.constructor("New Car "+ this.name);
}
CarFactory.prototype.toString=function(){
return 'Car Factory ' + this.name;
}
AudiFactory.prototype = new CarFactory(); // Here's where the inheritance occurs
AudiFactory.prototype.constructor=AudiFactory; // Otherwise instances of Audi would have a constructor of Car
function AudiFactory(name){
this.name=name;
}
AudiFactory.prototype.toString=function(){
return 'Audi Factory ' + this.name;
}
var myAudiFactory = new AudiFactory('');
alert('Hay your new ' + myAudiFactory + ' is ready.. Start Producing new audi cars !!! ');
var newCar = myAudiFactory.CreateNewCar(); // calls a method inherited from CarFactory
alert(newCar);
/*
Without resetting prototype constructor back to instance, new cars will not come from New Audi factory, Instead it will come from car factory ( base class ).. Dont we want our new car from Audi factory ????
*/
其他回答
得到了一个很好的代码示例,为什么真的有必要设置原型构造函数..
function CarFactory(name){
this.name=name;
}
CarFactory.prototype.CreateNewCar = function(){
return new this.constructor("New Car "+ this.name);
}
CarFactory.prototype.toString=function(){
return 'Car Factory ' + this.name;
}
AudiFactory.prototype = new CarFactory(); // Here's where the inheritance occurs
AudiFactory.prototype.constructor=AudiFactory; // Otherwise instances of Audi would have a constructor of Car
function AudiFactory(name){
this.name=name;
}
AudiFactory.prototype.toString=function(){
return 'Audi Factory ' + this.name;
}
var myAudiFactory = new AudiFactory('');
alert('Hay your new ' + myAudiFactory + ' is ready.. Start Producing new audi cars !!! ');
var newCar = myAudiFactory.CreateNewCar(); // calls a method inherited from CarFactory
alert(newCar);
/*
Without resetting prototype constructor back to instance, new cars will not come from New Audi factory, Instead it will come from car factory ( base class ).. Dont we want our new car from Audi factory ????
*/
下面是MDN的一个例子,我发现它对理解它的用法很有帮助。
在JavaScript中,我们有返回AsyncFunction对象的async函数。AsyncFunction不是一个全局对象,但可以通过使用构造函数属性检索它并利用它。
function resolveAfter2Seconds(x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
// AsyncFunction constructor
var AsyncFunction = Object.getPrototypeOf(async function(){}).constructor
var a = new AsyncFunction('a',
'b',
'return await resolveAfter2Seconds(a) + await resolveAfter2Seconds(b);');
a(10, 20).then(v => {
console.log(v); // prints 30 after 4 seconds
});
当你需要toString的替代而不需要monkeypatching时,它是必要的:
//Local foo = []; foo.toUpperCase = String(foo).toUpperCase; foo.push("a"); foo.toUpperCase(); //Global foo = []; window.toUpperCase = function (obj) {return String(obj).toUpperCase();} foo.push("a"); toUpperCase(foo); //Prototype foo = []; Array.prototype.toUpperCase = String.prototype.toUpperCase; foo.push("a"); foo.toUpperCase(); //toString alternative via Prototype constructor foo = []; Array.prototype.constructor = String.prototype.toUpperCase; foo.push("a,b"); foo.constructor(); //toString override var foo = []; foo.push("a"); var bar = String(foo); foo.toString = function() { return bar.toUpperCase(); } foo.toString(); //Object prototype as a function Math.prototype = function(char){return Math.prototype[char]}; Math.prototype.constructor = function() { var i = 0, unicode = {}, zero_padding = "0000", max = 9999; while (i < max) { Math.prototype[String.fromCharCode(parseInt(i, 16))] = ("u" + zero_padding + i).substr(-4); i = i + 1; } } Math.prototype.constructor(); console.log(Math.prototype("a") ); console.log(Math.prototype["a"] ); console.log(Math.prototype("a") === Math.prototype["a"]);
这是不必要的。这只是传统的OOP拥护者所做的许多事情之一,他们试图将JavaScript的原型继承转变为经典继承。唯一的事情是,下面
Student.prototype.constructor = Student;
你现在有了当前“构造函数”的引用。
在Wayne的答案中,它被标记为正确,你可以做与下面代码完全相同的事情
Person.prototype.copy = function() {
// return new Person(this.name); // just as bad
return new this.constructor(this.name);
};
使用下面的代码(只需替换此代码。构造师与Person)
Person.prototype.copy = function() {
// return new Person(this.name); // just as bad
return new Person(this.name);
};
感谢上帝,在ES6中,经典继承主义者可以使用语言的原生操作符,如class、extends和super,我们不必看到prototype。构造函数更正和父引用。
TLDR;不是非常必要,但从长远来看可能会有所帮助,这样做更准确。
注意:我之前的回答被编辑了很多,写得令人困惑,有一些错误,我在匆忙回答时漏掉了。感谢那些指出了一些严重错误的人。
基本上,它是在Javascript中正确地连接子类。当我们子类化时,我们必须做一些奇怪的事情来确保原型委托正确工作,包括覆盖原型对象。重写原型对象包括构造函数,因此我们需要修复引用。
让我们快速浏览一下ES5中的“类”是如何工作的。
假设你有一个构造函数和它的原型:
//Constructor Function
var Person = function(name, age) {
this.name = name;
this.age = age;
}
//Prototype Object - shared between all instances of Person
Person.prototype = {
species: 'human',
}
当你调用构造函数来实例化时,比如Adam:
// instantiate using the 'new' keyword
var adam = new Person('Adam', 19);
使用'Person'调用的new关键字基本上会运行Person构造函数,并附加几行代码:
function Person (name, age) {
// This additional line is automatically added by the keyword 'new'
// it sets up the relationship between the instance and the prototype object
// So that the instance will delegate to the Prototype object
this = Object.create(Person.prototype);
this.name = name;
this.age = age;
return this;
}
/* So 'adam' will be an object that looks like this:
* {
* name: 'Adam',
* age: 19
* }
*/
如果我们console.log(adam.species),查找将在adam实例中失败,并查找原型链到它的.prototype,即Person。原型和人物。prototype有一个.species属性,所以查找Person.prototype会成功。然后它将记录'human'。
在这里,Person.prototype.constructor将正确地指向Person。
现在有趣的部分,所谓的“子类化”。如果我们想创建一个Student类,它是Person类的一个子类,有一些额外的变化,我们需要确保Student.prototype.constructor指向Student以保证准确性。
这不是它自己做的。当你子类化时,代码看起来是这样的:
var Student = function(name, age, school) {
// Calls the 'super' class, as every student is an instance of a Person
Person.call(this, name, age);
// This is what makes the Student instances different
this.school = school
}
var eve = new Student('Eve', 20, 'UCSF');
console.log(Student.prototype); // this will be an empty object: {}
在这里调用new Student()将返回一个具有我们想要的所有属性的对象。在这里,如果我们检查einstanceof Person,它将返回false。如果我们试图接近夏娃。物种,它将返回undefined。
换句话说,我们需要连接委托,以便eve instanceof Person返回true,以便Student的实例正确地委托给Student。prototype,然后person。prototype。
但是,由于我们使用new关键字调用它,还记得调用添加了什么吗?它会调用object。create(Student。prototype)这就是我们在Student和Student。prototype之间建立委托关系的方式。现在请注意,学生。原型是空的。因此,查找.species Student实例将失败,因为它只委托给Student。在Student.prototype中不存在。species属性。
当我们分配Student时。prototype to Object.create(Person.prototype), Student。原型本身然后委托给Person。原型,和向上看的夏娃。物种会像我们期望的那样回归人类。假设我们希望它从Student继承。原型和人。所以我们需要解决这些问题。
/* This sets up the prototypal delegation correctly
*so that if a lookup fails on Student.prototype, it would delegate to Person's .prototype
*This also allows us to add more things to Student.prototype
*that Person.prototype may not have
*So now a failed lookup on an instance of Student
*will first look at Student.prototype,
*and failing that, go to Person.prototype (and failing /that/, where do we think it'll go?)
*/
Student.prototype = Object.create(Person.prototype);
现在委托工作了,但是我们覆盖了Student。Person.prototype的原型。如果我们调用student。prototype。构造函数时,它将指向Person而不是Student。这就是为什么我们要解决它。
// Now we fix what the .constructor property is pointing to
Student.prototype.constructor = Student
// If we check instanceof here
console.log(eve instanceof Person) // true
在ES5中,我们的构造函数属性是一个引用,它指向一个我们写的函数,目的是成为一个“构造函数”。除了new关键字给了我们什么,构造函数在其他方面是一个“普通”函数。
在ES6中,构造函数现在被内置到我们编写类的方式中——也就是说,当我们声明一个类时,它作为一个方法提供。这只是语法上的“糖”,但它确实给我们提供了一些方便,比如在扩展现有类时访问super。所以我们可以这样写上面的代码:
class Person {
// constructor function here
constructor(name, age) {
this.name = name;
this.age = age;
}
// static getter instead of a static property
static get species() {
return 'human';
}
}
class Student extends Person {
constructor(name, age, school) {
// calling the superclass constructor
super(name, age);
this.school = school;
}
}