简单来说,为什么我们使用prototype.constructor
。我正在阅读一篇关于继承的文章,我看到prototype.constructor
。当我评论该代码时,我发现结果没有区别。所以我的问题为何以及何时实际使用它。
function Mammal(name){
this.name=name;
this.action= function (){
alert('0')
}
}
function Cat(name){
this.name=name;
}
Cat.prototype = new Mammal();
//Cat.prototype.constructor=Cat; // Otherwise instances of Cat would have a constructor of Mammal
Cat.prototype.action=function(){
alert('1')
}
var y= new Mammal()
var x= new Cat()
y.action()
x.action()
答案 0 :(得分:2)
这主要是惯例。虽然JavaScript本身没有使用这不仅仅是惯例,请参阅¹了解详细信息。constructor
属性,但有时人们会在代码中使用它,假设它会引用回到对象的构造函数。
创建函数时:
function Cat() {
}
该函数从其prototype
属性上的一个对象开始,该对象具有一个名为constructor
的属性,该属性指向该函数:
console.log(Cat.prototype.constructor === Cat); // true
这是在规范中。 (这也是规范中提到属性的唯一位置 - 例如,JavaScript本身根本不使用此属性。不再是.¹)
因此,使用该原型创建的实例(无论是通过构造函数还是其他方式创建)都继承了constructor
属性:
var c = new Cat();
console.log(c.constructor === Cat); // true
var c2 = Object.create(Cat.prototype);
console.log(c2.constructor === Cat); // true, even though Cat wasn't used
替换函数上的prototype
属性时,正如通常在构建层次结构时所做的那样:
Cat.prototype = new Mammal(); // This is an anti-pattern, btw, see below
...您最终得到Cat.prototype
上的一个对象,其中constructor
指向Mammal
。由于这不是人们通常所期望的,所以习惯性地解决它:
Cat.prototype.constructor = Cat;
虽然JavaScript中没有任何内容使用属性(现在它已经¹),但有时人们会在代码中使用它,假设它会引用回对象的构造函数。
重新编写代码中的反模式:当使用构造函数构建层次结构时,实际上调用“基础”构造函数来创建“派生”构造函数{{1 }}。相反,使用prototype
创建原型:
Object.create
...然后链接到Cat.prototype = Object.create(Mammal.prototype);
Cat.prototype.constructor = Cat;
中的Mammal
:
Cat
为什么这样?考虑一下:如果基础需要参数,那么在构造时才能有意义地初始化实例会怎样?在拥有它们之前,你不能传递它。上面的模式允许您处理这种情况。
请注意,ES5中添加了function Cat() {
Mammal.call(this);
// ...
}
,因此某些旧浏览器(如IE8)缺少Object.create
。它的单参数版本可以通过简单的方式进行填充/填充:
if (!Object.create) {
Object.create = function(proto, props) {
if (typeof props !== "undefined") {
throw new Error("The second argument of Object.create cannot be shimmed.");
}
function f() { }
f.prototype = proto;
return new f;
};
}
我要注意,构造函数只是构建JavaScript中的对象层次结构的一种方式。它们不是唯一的方法,因为JavaScript使用原型继承。 JavaScript是如此强大,你可以使用构造函数来获得类似类继承的东西,但它也可以让你直接进行更传统的原型风格的继承。
¹“尽管JavaScript中没有任何内容使用constructor
属性...”从ES2015(又名“ES6”)开始,这不再是真的。现在,constructor
属性用于几个地方(例如SpeciesConstructor和ArraySpeciesCreate抽象操作),它们在各种类中使用,这些类具有返回类的新实例的方法,例如Array#slice
和Promise#then
。它在那些地方用于确保子类正常工作:例如,如果你继承Array
,并在子类的实例上使用slice
,则slice
返回的数组是一个实例您的子类,而不是原始Array
- 因为slice
使用ArraySpeciesCreate。