使用JavaScript的“新”方法创建对象,效果很好:
Vehicle.prototype = {}; // Not required I know
function Vehicle() {
}
Car.prototype = new Vehicle();
function Car() {
}
Ferrari.prototype = new Car();
function Ferrari() {
}
var o = new Ferrari();
console.log(o);
哪个输出:
> Ferrari
> __proto__ : Car
> __proto__ : Vehicle
> __proto__ : Object <- The one that isn't required
> __proto__ : Object <- Provided by JS
...
hasOwnProperty: function hasOwnProperty() { [native code] }
...
现在我想做同样的事情,避免使用新关键字,这就是我所拥有的:
Vehicle.prototype = {};
function Vehicle() {
var vehicle = Object.create(Vehicle.prototype);
return vehicle;
}
Car.prototype = Vehicle();
Car.prototype.constructor = Vehicle;
function Car() {
var car = Object.create(Car.prototype);
return car;
}
Ferrari.prototype = Car();
Ferrari.prototype.constructor = Car;
function Ferrari() {
var ferrari = Object.create(Ferrari.prototype);
//ferrari.constructor = Ferrari; <- Lookey here - commented line
return ferrari;
}
var o = new Ferrari();
console.log(o);
哪个输出:
> **Car**
> __proto__ : Car
> constructor : function Car()
__proto__ : Vehicle
> constructor : function Vehicle()
__proto__ : Object <- The one that isn't required
> __proto__ : Object
...
hasOwnProperty: function hasOwnProperty() { [native code] }
...
注意输出的第一行现在是Car,而不是法拉利。这可以通过删除注释行,或通过更改法拉利的原型来纠正,如下所示:
var realPrototype = Car();
realPrototype.constructor = Car;
Ferrari.prototype = Object.create(realPrototype);
Ferrari.prototype.constructor = Ferrari;
答案 0 :(得分:0)
为什么第一种方法正确识别对象为法拉利? (在Chrome中测试)
我是否理解第一个示例中有一些隐藏的“构造函数”属性,如果是这样,它们在哪里,以及它们放在哪里?在我没有明确放入的第二个例子中是否有任何隐藏的构造函数属性?
是的,如果您分配给函数的prototype
属性,或者以某种方式创建新实例,Chrome似乎会设置隐藏的“构造函数”属性。没有完全弄明白。
它们在哪里?可能只有console.log
可以访问的一些内部属性。但是,您似乎能够使用显式的“构造函数”属性覆盖它们,至少对于显示__proto__
个对象。
我想这是由Chrome完成的,因为很多人忘记在覆盖原型对象时设置构造函数属性(因为it's actually useless),尽管它is recommended - 另见JavaScript inheritance and the constructor property。 Chrome Devs希望制作一个漂亮的log
函数来指示对象的类型,因此他们需要解决人们的懒惰问题。
我是否正确地在两种方法中进行继承?
代码中的继承没有问题。新创建的Car
实例具有令人满意的原型链,使用两种方法。
尽管如此,还是存在一些陷阱。不要使用new
关键字来创建原型对象实例。您不需要在其上应用构造函数的完整实例。只需使用Car.prototype = Object.create(Vehicle.prototype)
;另见What is the reason to use the 'new' keyword at Derived.prototype = new Base
此外,您没有设置“构造函数”属性,或者做错了。在原型对象上定义的属性由对new
调用相应构造函数创建的所有对象继承,它应该为它们提供对该函数的精确引用。对于任何创建的函数,默认情况下都会这样做,请参阅EcmaScript specification §13.2 steps 16-18。
如果您重新分配到fn.prototype
,则会覆盖该行为(无论是对象文字,new
构造还是Object.create
调用。实际上,“构造函数”属性是inherited from an object higher up in the prototype chain - 在您的第一个示例中o.constructor
是Object
,继承自空对象文字。因此,在您的情况下,我们需要在分配后重置它:
Vehicle.prototype.constructor = Vehicle;
Car.prototype.constructor = Car;
Ferrari.prototype.constructor = Ferrari;
// or, we combine it with the Object.create call:
Car.prototype = Object.create(Vehicle.prototype, {
constructor: {value:Car, configurable:true, writable:true}
});
请注意,不需要在构造函数中显式设置它(如第二个示例中所示),在对象上创建一个额外的属性。这就是继承的目的。
或者我们根本不关心“构造函数”属性:
我应该使用最后两种方法中的哪一种来将物体识别为法拉利?或者我不应该在乎 - 毕竟“instanceof”仍然有用吗?
要将对象识别为法拉利总是使用instanceof
。 “构造函数”属性不可靠。它可能已被遗忘设置,或者它不是您在比较中所期望的:像someObj.constructor === Car
这样的“类型检查”对于法拉利或任何其他正确设置其构造函数属性的“子类”都不起作用。它适用于instanceof
。
答案 1 :(得分:-1)
为什么第一种方法正确地将对象识别为法拉利?
因为对象o
的构造函数是函数Ferrari
。
我是否正确地在两种方法中进行继承?
你在第一种方法中正确地做到了,但在第二种方法中做得不正确。
提供我已正确完成继承,我应该使用最后两种方法中的哪一种来将对象识别为 法拉利?或者我不应该关心 - 毕竟“instanceof”仍然有用吗?
为了统一,你应该在函数体的中的每个原型上设置构造函数属性,这样你就不必这样做了:
Car.prototype = Vehicle();
Car.prototype.constructor = Vehicle;
Aeroplane.prototype = Vehicle();
Aeroplane.prototype.constructor = Vehicle;
...
相反,你应该使用:
function Vehicle() {
var vehicle = Object.create(Vehicle.prototype);
vehicle.constructor = Vehicle;
return vehicle;
}
Car.prototype = Vehicle();
function Car() {
var car = Object.create(Car.prototype);
car.constructor = Car;
return car;
}
Ferrari.prototype = Car();
function Ferrari() {
var ferrari = Object.create(Ferrari.prototype);
ferrari.constructor = Ferrari;
return ferrari;
}
var o = new Ferrari();
console.log(o);
这正确地导致了与第一个示例中的结构类似的结构。