在MDN文章《面向对象Javascript介绍》中关于继承的部分,我注意到他们设置了prototype.constructor:

// correct the constructor pointer because it points to Person
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对象。

其他回答

它并不总是必要的,但它确实有它的用途。假设我们想在基类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

这有什么重要的目的吗?

是也不是。

在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。

我不同意。不需要设置原型。取完全相同的代码,但去掉原型。构造函数。有什么变化吗?不。现在,进行以下更改:

Person = function () {
    this.favoriteColor = 'black';
}

Student = function () {
    Person.call(this);
    this.favoriteColor = 'blue';
}

在测试代码的末尾……

alert(student1.favoriteColor);

颜色是蓝色的。

对原型的更改。构造函数,根据我的经验,没有做太多,除非你做非常具体,非常复杂的事情,这可能不是一个好的实践:)

Edit: After poking around the web for a bit and doing some experimentation, it looks like people set the constructor so that it 'looks' like the thing that is being constructed with 'new'. I guess I would argue that the problem with this is that javascript is a prototype language - there is no such thing as inheritence. But most programmers come from a background of programming that pushes inheritence as 'the way'. So we come up with all sorts of things to try and make this prototypical language a 'classic' language.. such as extending 'classes'. Really, in the example they gave, a new student is a person - it isn't 'extending' from another student.. the student is all about the person, and whatever the person is the student is as well. Extend the student, and whatever you've extended is a student at heart, but is customized to fit your needs.

克罗克福德有点疯狂和过分热心,但认真阅读他写的一些东西。它会让你从不同的角度看待这个问题。

现在不需要加糖的函数'类'或使用'New'。使用对象字面量。

Object原型已经是一个“类”。当你定义一个对象文字时,它已经是原型对象的一个实例。这些也可以作为另一个对象的原型,等等。

const Person = {
  name: '[Person.name]',
  greeting: function() {
    console.log( `My name is ${ this.name || '[Name not assigned]' }` );
  }
};
// Person.greeting = function() {...} // or define outside the obj if you must

// Object.create version
const john = Object.create( Person );
john.name = 'John';
console.log( john.name ); // John
john.greeting(); // My name is John 
// Define new greeting method
john.greeting = function() {
    console.log( `Hi, my name is ${ this.name }` )
};
john.greeting(); // Hi, my name is John

// Object.assign version
const jane = Object.assign( Person, { name: 'Jane' } );
console.log( jane.name ); // Jane
// Original greeting
jane.greeting(); // My name is Jane 

// Original Person obj is unaffected
console.log( Person.name ); // [Person.name]
console.log( Person.greeting() ); // My name is [Person.name]

这里值得一读:

Class-based object-oriented languages, such as Java and C++, are founded on the concept of two distinct entities: classes and instances. ... A prototype-based language, such as JavaScript, does not make this distinction: it simply has objects. A prototype-based language has the notion of a prototypical object, an object used as a template from which to get the initial properties for a new object. Any object can specify its own properties, either when you create it or at run time. In addition, any object can be associated as the prototype for another object, allowing the second object to share the first object's properties

得到了一个很好的代码示例,为什么真的有必要设置原型构造函数..

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 ???? 
*/