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

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

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


当前回答

下面是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
});

其他回答

这有什么重要的目的吗?

是也不是。

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

给定简单构造函数:

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被第三方使用时,我们肯定应该这样做,但当代码最终在运行时执行时,我们不应该这样做。

这是必要的。类继承中的任何类都必须有自己的构造函数,在原型继承中也是如此。它也方便对象的构建。但这个问题是不必要的,真正需要理解的是在JavaScript世界中调用函数作为构造函数的效果和解析对象属性的规则。

使用表达式new <函数名>([parameters])将函数作为构造函数执行的效果

创建类型名称为函数名的对象 函数中的内部属性附加到创建的对象 函数的属性原型作为原型自动附加到创建的对象上

对象属性解析规则

不仅要在对象上查找属性,还要在对象的原型、原型的原型上查找属性,以此类推,直到找到具有匹配名称的属性或到达原型链的末尾。

基于这些底层机制,语句<构造函数名称>.prototype. conf。构造函数= <构造函数名称>等于在构造函数体中附加表达式this的效果。构造函数= <构造函数名称>。如果第二次发声,构造函数将在对象上解析;如果第一次发声,构造函数将在对象的原型上解析。

当你需要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"]);

到目前为止,困惑仍然存在。

按照原来的例子,你有一个现有的对象student1 as:

var student1 = new Student("Janet", "Applied Physics");

假设你不想知道student1是如何创建的,你只是想要另一个类似的对象,你可以使用student1的构造函数属性:

var student2 = new student1.constructor("Mark", "Object-Oriented JavaScript");

在这里,如果没有设置构造函数属性,它将无法从Student获取属性。相反,它将创建一个Person对象。