3

问题:

  1. 为什么第一种方法能正确识别物体为法拉利?(在 Chrome 中测试)
  2. 我在这两种方法中正确地进行继承吗?
  3. 我是否正确理解第一个示例中存在一些隐藏的“构造函数”属性,如果是,它们在哪里,是什么把它们放在那里?在我没有明确输入的第二个示例中是否有任何隐藏的构造函数属性?
  4. 如果我已经正确地完成了继承,我应该使用最后两种方法中的哪一种来让对象被识别为法拉利?或者我应该不在乎 - 毕竟“instanceof”仍然有效?

语境:

使用 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] }
          ...

现在我想做同样的事情来避免使用 new 关键字,这就是我所拥有的:

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,而不是 Ferrari。这可以通过删除注释行来纠正,或者通过改变法拉利的原型如下:

var realPrototype = Car();
realPrototype.constructor = Car;

Ferrari.prototype = Object.create(realPrototype);
Ferrari.prototype.constructor = Ferrari;
4

2 回答 2

0

为什么第一种方法能正确识别物体为法拉利?(在 Chrome 中测试)

我是否正确理解第一个示例中存在一些隐藏的“构造函数”属性,如果是,它们在哪里,是什么把它们放在那里?在我没有明确输入的第二个示例中是否有任何隐藏的构造函数属性?

是的,如果您分配给函数的prototype属性或以某种方式创建新实例,Chrome 似乎会设置一个隐藏的“构造函数”属性。具体还没有弄清楚。

他们在哪里?可能是一些只能console.log访问的内部属性。然而,您似乎能够用显式的“构造函数”属性覆盖它们,至少对于__proto__对象的显示。

我猜这是由 Chrome 完成的,因为许多人在覆盖原型对象时忘记设置构造函数属性(因为它实际上是无用的),尽管它是推荐的- 另见JavaScript 继承和构造函数属性。Chrome 开发人员想要制作一个log指示对象类型的漂亮函数,因此他们需要解决人们的懒惰问题。

我在这两种方法中正确地进行继承吗?

您的代码中的继承没有问题。新创建Car的实例具有期望的原型链,具有两种方法。

不过,也有一些陷阱。不要使用new关键字来创建原型对象实例。您不想要完整的实例,并在它们上应用构造函数。只需使用Car.prototype = Object.create(Vehicle.prototype); 另请参阅在 Derived.prototype = new Base 使用“new”关键字的原因是什么的答案

此外,您没有设置“构造函数”属性,或者做错了。在原型对象上定义的该属性由调用相应构造函数创建的所有对象继承new,并且应该为它们提供对该函数的引用。对于任何创建的函数,默认情况下都会执行此操作,请参阅EcmaScript 规范 §13.2 步骤 16-18

如果您重新分配给fn.prototype,您将覆盖该行为(无论它是对象文字、new构造还是Object.create调用)。实际上,“构造函数”属性然后从原型链中更高的对象继承- 在您的第一个示例o.constructorObject,是从空对象文字继承的。因此,在您的情况下,我们需要在分配后重置它:

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.

于 2012-12-09T23:05:03.047 回答
-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);

这正确地导致了与您的第一个示例中的结构相似的结构。

于 2012-12-09T18:36:31.520 回答