JavaScript继承和构造函数属性
考虑以下代码JavaScript继承和构造函数属性,javascript,inheritance,constructor,instanceof,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() 为什么b和c的构造函数没有更新 我做错
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()
- 为什么b和c的构造函数没有更新
- 我做错了吗
- 更新构造函数的最佳方法是什么
进一步,请考虑以下内容:
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()
是对象。getPrototypeOf(new c())
,那么a{}
怎么可能知道instanceof
是new c()
的实例呢c
function b() {}
然后b.prototype
有一个.constructor
属性,该属性自动设置为b
。但是,您当前正在覆盖原型,从而丢弃该变量:
b.prototype = new a;
然后b.prototype
不再具有.constructor
属性;它被覆盖删除了。它确实继承自a
,并且(新的a)。构造函数===a
,因此(新的b)。构造函数===a
(它在原型链中引用相同的属性)
最好的方法是在之后手动设置:
b.prototype.constructor = b;
您还可以为此制作一个小功能:
function inherit(what, from) {
what.prototype = new from;
what.prototype.constructor = what;
}
构造函数
是函数对象的原型
属性的默认值的常规、不可枚举属性。因此,分配给prototype
将丢失该属性
instanceof
仍将工作,因为它不使用构造函数
,而是扫描对象的原型链,查找函数的prototype
属性的(当前)值,即foo instanceof foo
等价于
var proto = Object.getPrototypeOf(foo);
for(; proto !== null; proto = Object.getPrototypeOf(proto)) {
if(proto === Foo.prototype)
return true;
}
return false;
在ECMAScript 3中,无法设置与内置属性行为相同的构造函数
属性,因为用户定义的属性始终是可枚举的(即对中的..可见)
ECMAScript 5改变了这一点。但是,即使您手动设置构造函数
,您的代码仍然存在问题:特别是,将原型
设置为父类的实例是一个坏主意-定义子类时不应调用父类构造函数,而应在创建子实例时调用父类构造函数
下面是一些ECMAScript 5示例代码,介绍了如何执行此操作:
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.';
};
如果坚持使用ECMAScript 3,则需要使用自定义对象,而不是对象。create()
,并且无法使构造函数
不可枚举:
Cat.prototype = clone(Pet.prototype);
Cat.prototype.constructor = Cat;
好的,让我们玩一个小智力游戏:
从上图中我们可以看到:
当我们创建像function Foo(){}
这样的函数时,JavaScript会创建一个function
实例
每个函数
实例(构造函数)都有一个属性原型
,它是一个指针
构造函数的prototype
属性指向它的prototype对象
原型对象有一个属性构造函数
,它也是一个指针
原型对象的构造函数
属性指向其构造函数
当我们创建Foo
的新实例时,比如newfoo()
,JavaScript会创建一个新对象
实例的内部[[proto]]
属性指向构造函数的原型
现在,问题出现了,为什么JavaScript不将构造函数
属性附加到实例对象而不是原型。考虑:
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
正如您所看到的,构造函数
属性只是原型的另一种方法,就像上面示例中的区域
。构造函数
属性的特殊之处在于它用于初始化原型的实例。否则,它与原型的任何其他方法完全相同
在原型上定义构造函数
属性是有利的,原因如下:
逻辑上是正确的。例如,考虑<代码>对象。原型< /COD>。对象的构造函数
属性。原型
指向对象
。如果在实例上定义了constructor
属性,则Object.prototype.constructor
将是未定义的
,因为Object.prototype
是null
的实例
它与其他原型方法没有区别。这使得new
的工作更容易,因为它不需要在每个实例上定义constructor
属性
每个实例共享相同的构造函数
属性。因此它是有效的
现在,当我们谈论继承时,我们有以下场景:
从上图中我们可以看到:
派生构造函数的prototype
属性设置为基本构造函数的实例
因此,派生构造函数实例的内部[[proto]]
属性也指向它
因此,派生构造函数实例的构造函数
属性现在指向基构造函数
至于instanceof
操作符,与流行的观点相反,它不依赖于实例的构造函数
属性。从上面可以看出,这将导致错误的结果
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.\uuuu proto\uuuu==foo.prototype
foo.\uuuuuuuuuuuuuuuuuuuuuuuuu==Bar.prototype
foo.\uuuuu proto.\uuuuu proto.\uuuuuu proto.\uuuuuuuu__