请考虑以下代码。
function a() {}
function b() {}
function c() {}
b.prototype = new a();
c.prototype = new b();
console.log((new a()).constructor); //a()
console.log((new b()).constructor); //a()
console.log((new c()).constructor); //a()
此外,请考虑以下事项。
console.log(new a() instanceof a); //true
console.log(new b() instanceof b); //true
console.log(new c() instanceof c); //true
(new c()).constructor
等于a()
且Object.getPrototypeOf(new c())
为a{ }
,instanceof
如何知道new c()
是c
{{1}}的实例?答案 0 :(得分:64)
好的,让我们玩一个小小的心灵游戏:
从上图中我们可以看到:
function Foo() {}
这样的函数时,JavaScript会创建一个Function
实例。Function
实例(构造函数)都有一个属性prototype
,它是一个指针。prototype
属性指向其原型对象。constructor
,它也是一个指针。constructor
属性指向其构造函数。Foo
的新new Foo()
实例时,JavaScript会创建一个新对象。[[proto]]
属性指向构造函数的原型。现在,问题出现了为什么JavaScript不将constructor
属性附加到实例对象而不是原型。考虑:
function defclass(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
var Square = defclass({
constructor: function (side) {
this.side = side;
},
area: function () {
return this.side * this.side;
}
});
var square = new Square(10);
alert(square.area()); // 100
正如您所看到的,constructor
属性只是原型的另一种方法,如上例中的area
。 constructor
属性的特殊之处在于它用于初始化原型的实例。否则它与原型的任何其他方法完全相同。
在原型上定义constructor
属性是有利的,原因如下:
Object.prototype
。 constructor
的{{1}}属性指向Object.prototype
。如果在实例上定义了Object
属性,则constructor
将为Object.prototype.constructor
,因为undefined
是Object.prototype
的实例。null
的工作更容易,因为它不需要在每个实例上定义new
属性。constructor
属性。因此它很有效率。现在,当我们谈论继承时,我们有以下情况:
从上图中我们可以看到:
constructor
属性设置为基础构造函数的实例。prototype
属性也指向它。[[proto]]
属性现在指向基础构造函数。对于constructor
运算符,与普遍认为的相反,它不依赖于实例的instanceof
属性。从上面我们可以看出,这会导致错误的结果。
constructor
运算符是二元运算符(它有两个操作数)。它在实例对象和构造函数上运行。作为Mozilla Developer Network的解释,它只是执行以下操作:
instanceof
简单地说,如果function instanceOf(object, constructor) {
while (object != null) {
if (object == constructor.prototype) { //object is instanceof constructor
return true;
} else if (typeof object == 'xml') { //workaround for XML objects
return constructor.prototype == XML.prototype;
}
object = object.__proto__; //traverse the prototype chain
}
return false; //object is not instanceof constructor
}
继承自Foo
,那么Bar
实例的原型链就是:
Foo
foo.__proto__ === Foo.prototype
foo.__proto__.__proto__ === Bar.prototype
foo.__proto__.__proto__.__proto__ === Object.prototype
如您所见,每个对象都继承自foo.__proto__.__proto__.__proto__.__proto__ === null
构造函数。当内部Object
属性指向[[proto]]
时,原型链结束。
null
函数只是遍历实例对象的原型链(第一个操作数),并将每个对象的内部instanceof
属性与构造函数的[[proto]]
属性进行比较(第二个操作数)。如果匹配,则返回prototype
;如果原型链结束,则返回true
。
答案 1 :(得分:12)
默认情况下,
function b() {}
然后b.prototype
具有.constructor
属性,该属性自动设置为b
。但是,您目前正在覆盖原型,从而丢弃该变量:
b.prototype = new a;
然后b.prototype
不再具有.constructor
属性了;它被覆盖了。 继承自a
,(new a).constructor === a
,因此(new b).constructor === a
(它指的是原型链中的相同属性)。
最好的办法是简单地手动设置:
b.prototype.constructor = b;
你也可以为此做一点功能:
function inherit(what, from) {
what.prototype = new from;
what.prototype.constructor = what;
}
答案 2 :(得分:5)
constructor
是函数对象的prototype
属性的默认值的常规非枚举属性。因此,分配给prototype
将失去财产。
instanceof
仍然有用,因为它不使用constructor
,而是扫描对象的原型链以获取函数prototype
属性的(当前)值,即{{ 1}}相当于
foo instanceof Foo
在ECMAScript3中,没有办法设置一个var proto = Object.getPrototypeOf(foo);
for(; proto !== null; proto = Object.getPrototypeOf(proto)) {
if(proto === Foo.prototype)
return true;
}
return false;
属性,其行为与内置属性相同,因为用户定义的属性总是可枚举的(即constructor
可见)。
这改变了ECMAScript5。但是,即使您手动设置for..in
,您的代码仍然存在问题:特别是,将constructor
设置为parent-'class'的实例是一个坏主意 - 父构造函数不应该在定义child-'class'时调用,而是在创建子实例时调用。
这是一些ECMAScript5示例代码,用于完成该操作:
prototype
如果您坚持使用ECMAScript3,则需要使用自定义clone()
function代替function Pet(name) {
this.name = name;
}
Pet.prototype.feed = function(food) {
return this.name + ' ate ' + food + '.';
};
function Cat(name) {
Pet.call(this, name);
}
Cat.prototype = Object.create(Pet.prototype, {
constructor : {
value : Cat,
writable : true,
enumerable : false,
configurable : true
}
});
Cat.prototype.caress = function() {
return this.name + ' purrs.';
};
,并且无法使Object.create()
无法枚举:
constructor