Quo*_*ons 36 javascript inheritance constructor instanceof
请考虑以下代码.
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()
Run Code Online (Sandbox Code Playgroud)
此外,请考虑以下内容.
console.log(new a() instanceof a); //true
console.log(new b() instanceof b); //true
console.log(new c() instanceof c); //true
Run Code Online (Sandbox Code Playgroud)
(new c()).constructor等于a()和Object.getPrototypeOf(new c())是a{ },怎么可能instanceof知道这new c()是一个实例c?Aad*_*hah 64
好吧,让我们玩一个小小的心灵游戏:

从上图中我们可以看到:
function Foo() {},JavaScript会创建一个Function实例.Function实例(构造函数)都有一个属性prototype,它是一个指针.prototype构造函数的属性指向其原型对象.constructor,它也是一个指针.constructor原型对象的属性指向其构造函数.Foolike 的新实例时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
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,该constructor属性只是原型的另一种方法,area如上例所示.该constructor属性的特殊之处在于它用于初始化原型的实例.否则它与原型的任何其他方法完全相同.
constructor由于以下原因,在原型上定义属性是有利的:
Object.prototype.点的constructor属性.如果财产上的实例定义,那么会因为是的一个实例.Object.prototypeObjectconstructorObject.prototype.constructorundefinedObject.prototypenullnew更容易,因为它不需要constructor在每个实例上定义属性.constructor属性.因此它很有效率.现在,当我们讨论继承时,我们有以下场景:

从上图中我们可以看到:
prototype属性设置为基础构造函数的实例.[[proto]]派生构造函数实例的内部属性也指向它.constructor派生的构造函数实例的属性现在指向基础构造函数.至于instanceof运营商,与流行的看法相反,它不依赖于constructor实例的属性.从上面我们可以看出,这将导致错误的结果.
该instanceof操作是一个二元运算符(它有两个操作数).它在实例对象和构造函数上运行.作为Mozilla Developer Network的解释,它只是执行以下操作:
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
}
Run Code Online (Sandbox Code Playgroud)
简单地说,如果Foo继承自Bar,那么实例的原型链Foo将是:
foo.__proto__ === Foo.prototypefoo.__proto__.__proto__ === Bar.prototypefoo.__proto__.__proto__.__proto__ === Object.prototypefoo.__proto__.__proto__.__proto__.__proto__ === null如您所见,每个对象都继承自Object构造函数.当内部[[proto]]属性指向时,原型链结束null.
该instanceof函数只是遍历实例对象的原型链(第一个操作数),并将[[proto]]每个对象的内部属性prototype与构造函数的属性(第二个操作数)进行比较.如果匹配,则返回true; 如果原型链结束,它返回false.
pim*_*vdb 12
默认情况下,
function b() {}
Run Code Online (Sandbox Code Playgroud)
然后b.prototype有一个自动.constructor设置的属性b.但是,您目前正在覆盖原型,从而丢弃该变量:
b.prototype = new a;
Run Code Online (Sandbox Code Playgroud)
然后再b.prototype没有.constructor财产了; 它被覆盖了.它确实继承了a,(new a).constructor === a因此(new b).constructor === a(它指的是原型链中的相同属性).
最好的办法是简单地手动设置:
b.prototype.constructor = b;
Run Code Online (Sandbox Code Playgroud)
你也可以为此做一点功能:
function inherit(what, from) {
what.prototype = new from;
what.prototype.constructor = what;
}
Run Code Online (Sandbox Code Playgroud)
constructor是prototype函数对象属性的默认值的常规非枚举属性.因此,分配prototype将失去财产.
instanceof仍将工作,因为它不使用constructor,而是扫描对象的原型链为函数的prototype属性的(当前)值,即foo instanceof Foo相当于
var proto = Object.getPrototypeOf(foo);
for(; proto !== null; proto = Object.getPrototypeOf(proto)) {
if(proto === Foo.prototype)
return true;
}
return false;
Run Code Online (Sandbox Code Playgroud)
在ECMAScript3中,没有办法设置一个constructor与内置属性相同的属性,因为用户定义的属性总是可枚举的(即可见for..in).
这改变了ECMAScript5.但是,即使您constructor手动设置,您的代码仍然存在问题:特别是,设置prototype为parent-'class'的实例是一个坏主意- 当child-'class'为时,不应调用父构造函数定义,而是在创建子实例时.
这是一些ECMAScript5示例代码,用于完成它的工作方式:
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.';
};
Run Code Online (Sandbox Code Playgroud)
如果您坚持使用ECMAScript3,则需要使用自定义clone()函数,而Object.create()不能使用constructor不可枚举的函数:
Cat.prototype = clone(Pet.prototype);
Cat.prototype.constructor = Cat;
Run Code Online (Sandbox Code Playgroud)