Javascript 为什么需要设置原型构造函数?

Javascript 为什么需要设置原型构造函数?,javascript,oop,inheritance,Javascript,Oop,Inheritance,在中,我注意到他们设置了prototype.constructor: // correct the constructor pointer because it points to Person Student.prototype.constructor = Student; // correct the constructor pointer because it points to Person Student.prototype.constructor = Student; 这

在中,我注意到他们设置了prototype.constructor:

// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;  
// correct the constructor pointer because it points to Person  
Student.prototype.constructor = Student;
这有什么重要意义吗?省略它可以吗?

编辑,实际上我错了。注释这一行根本不会改变它的行为。(我测试了一下)
是的,这是必要的。当你这样做的时候

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()
中设置任何属性。没有必要设置原型。获取完全相同的代码,但删除prototype.constructor行。有什么变化吗?否。现在,进行以下更改:

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

Student = function () {
    Person.call(this);
    this.favoriteColor = 'blue';
}
在测试代码的末尾

alert(student1.favoriteColor);
颜色是蓝色的

根据我的经验,对prototype.constructor的更改不会有多大作用,除非您正在做非常具体、非常复杂的事情,而这些事情可能不是很好的实践:)

编辑: 在浏览了一下网页并做了一些实验之后,人们似乎设置了构造器,使其“看起来”像是用“new”构造的东西。我想我会说,这个问题在于javascript是一种原型语言——没有继承这回事。但是大多数程序员的编程背景都将继承作为“方法”。因此,我们想出了各种各样的办法,试图使这种原型语言成为一种“经典”语言。。例如扩展“类”。事实上,在他们给出的例子中,一个新学生是一个人,而不是从另一个学生那里“延伸”。。学生就是这个人,不管这个人是什么,学生也是。扩展学生,无论你扩展了什么,在本质上都是一个学生,但都是根据你的需要定制的


Crockford有点疯狂和过分热情,但是认真阅读他写的一些东西。。这会让你对这些东西有不同的看法。

这并不总是必要的,但它确实有它的用途。假设我们想在base
Person
类上创建一个copy方法。像这样:

// 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);
现在,当我们创建一个新的
学生并复制它时会发生什么

var student1 = new Student("trinth");  
console.log(student1.copy() instanceof Student); // => false
该副本不是
学生
的实例。这是因为(没有显式检查),我们无法从“基类”返回
Student
副本。我们只能返回
个人
。但是,如果我们重置了构造函数:

// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;  
// 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

得到了一个很好的代码示例,说明了为什么设置原型构造函数是非常必要的

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

这有一个巨大的陷阱,如果你写

Student.prototype.constructor = Student;
但如果有一位老师,他的原型也是人,你写

Teacher.prototype.constructor = Teacher;
那么学生构装师现在就是老师了

编辑: 您可以通过确保使用Object.create创建的Person类的新实例设置学生和教师原型来避免这种情况,如Mozilla示例中所示

Student.prototype = Object.create(Person.prototype);
Teacher.prototype = Object.create(Person.prototype);

到目前为止,仍然存在混乱

按照原始示例,由于现有对象
student1
为:

var student1 = new Student("Janet", "Applied Physics");
假设您不想知道
student1
是如何创建的,您只需要另一个类似的对象,您可以使用
student1
的构造函数属性,例如:

var student2 = new student1.constructor("Mark", "Object-Oriented JavaScript");
在这里,如果未设置构造函数属性,它将无法从
Student
获取属性。相反,它将创建一个
Person
对象

这有什么重要意义吗

是和否

在ES5和更早的版本中,JavaScript本身没有对任何东西使用
构造函数。它定义了函数的
prototype
属性上的默认对象将拥有它,并且它将引用回函数,就是它。规范中没有任何其他内容提及它

这在ES2015(ES6)中发生了变化,它开始在继承层次结构中使用它。例如,在构建要返回的新承诺时,使用您调用它的承诺(via)的
构造函数
属性。它还涉及到子类型数组(via)

在语言本身之外,有时人们会在尝试构建通用“克隆”函数时使用它,或者只是在他们想引用他们认为是对象的构造函数时使用它。我的经验是,使用它是罕见的,但有时人们确实使用它

可以省略吗

默认情况下,它就在那里,您只需要在替换函数的
prototype
属性上的对象时将其放回:

Student.prototype = Object.create(Person.prototype);
如果不这样做:

Student.prototype.constructor = Student;
…然后
Student.prototype.constructor
继承自
Person.prototype
,它(大概)具有
constructor=Person
。所以这是误导。当然,如果您正在对使用它的东西进行子类化(如
Promise
Array
),而没有使用
class
1(它为您处理这个问题),您需要确保设置正确。所以基本上:这是个好主意

如果您的代码(或您使用的库代码)中没有任何内容使用它,这也没关系。我一直确保它的接线正确

当然,有了ES2015(又称ES6)的
class
关键字,大多数时候我们都会使用它,我们不必再使用它了,因为当我们使用它时,它是为我们处理的

class Student extends Person {
}

“如果您正在对使用它的东西进行子类化(如
Promise
Array
),而不使用
class
”,则它是pos
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
 * }
 */
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: {}
/* 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);
// 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
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;
   }
}
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]
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(){...}
Student.prototype.constructor = Student; 
Person.prototype.copy = function() {  
    // return new Person(this.name); // just as bad
    return new this.constructor(this.name);
};  
Person.prototype.copy = function() {  
    // return new Person(this.name); // just as bad
    return new Person(this.name);
}; 
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
});