JavaScript继承 - 我做得对吗?

时间:2012-12-09 18:12:52

标签: javascript inheritance prototype

问题:

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

    使用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;
    

2 个答案:

答案 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.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

答案 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);

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