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

// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;  

这有什么重要的目的吗?可以省略吗?


当前回答

给定简单构造函数:

function Person(){
    this.name = 'test';
}


console.log(Person.prototype.constructor) // function Person(){...}

Person.prototype = { //constructor in this case is Object
    sayName: function(){
        return this.name;
    }
}

var person = new Person();
console.log(person instanceof Person); //true
console.log(person.sayName()); //test
console.log(Person.prototype.constructor) // function Object(){...}

默认情况下(来自规范https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor),所有原型都会自动获得一个称为constructor的属性,该属性指向属性所在的函数。 根据构造函数的不同,可能会将其他属性和方法添加到原型中,这不是很常见的做法,但仍然允许进行扩展。

所以简单地回答:我们需要确保原型中的值。构造函数被正确地设置为规范所假定的样子。

我们必须总是正确地设置这个值吗?它有助于调试,并使内部结构与规范保持一致。当我们的API被第三方使用时,我们肯定应该这样做,但当代码最终在运行时执行时,我们不应该这样做。

其他回答

现在不需要加糖的函数'类'或使用'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

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

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.

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

这是不必要的。这只是传统的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;
   }
}

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

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