使用object.create()

时间:2015-07-02 03:33:31

标签: javascript inheritance prototype

我正在研究MDN关于继承和原型的两个例子。鉴于这两个例子,我的理解似乎存在一些冲突 - 它们似乎是矛盾的:

var a = {a: 1};
//inheritance looks like: a ---> Object.prototype ---> null
var b = Object.create(a);
//inheritance looks like: b ---> a ---> Object.prototype ---> null
console.log(b.a); // 1 (inherited)

到目前为止有道理,但在另一页上,了解.call()方法:

function Product(name, price) {
  this.name = name;
  this.price = price;

  if (price < 0) {
    throw RangeError('Cannot create product ' +
                      this.name + ' with a negative price');
  }

  return this;
}

function Food(name, price) {
  Product.call(this, name, price);
  this.category = 'food';
}

Food.prototype = Object.create(Product.prototype);

function Toy(name, price) {
  Product.call(this, name, price);
  this.category = 'toy';
}

Toy.prototype = Object.create(Product.prototype);

var cheese = new Food('feta', 5);
var fun = new Toy('robot', 40);

Food的原型现在不是产品的原型吗?即Function.prototype?

我在期待:

Food.prototype = Object.create(Product)

这是否与它的功能有关?

谢谢,

2 个答案:

答案 0 :(得分:1)

你似乎对第二个例子有误解。特别是,我认为你可能在“构造函数”和“原型”之间存在一些混淆。我将通过这个例子来解释。

首先,该示例声明了一个函数Product。这是一个普通的函数,但它引用this的事实表明它的目的是用作构造函数。请注意,return this语句是多余的,因为当作为构造函数调用时,函数的返回值将被忽略(即使用new)。

Product本身就是一个对象 - 它是一个函数,函数是对象。所有对象都有一个原型,与您使用Product.prototype获得的原型不同 - 您可以使用Object.getPrototypeOf(Product)访问它,并且您会发现它等于Function.prototype。所有对象也都有一个构造函数,可以使用Product.constructor获得。由于product是一个函数,因此它的构造函数是Function。构造函数始终是一个函数。

然后,prototype属性是什么?并非所有对象都具有此功能 - 仅有功能。它不是Product本身的原型。它是通过new Product(name,price)创建的任何对象的原型(即Object.getPrototypeOf(new Product(name,price))== Product.prototype)。如果您想将Product视为一个类,那么该类的原型是Product.prototype。什么是Product.prototype的构造函数?很容易确认Product.prototype.constructor == Product

那么,如果你做Food.prototype = Object.create(Product)而不是Food.prototype = Object.create(Product.prototype)会怎么样?

  1. Object.create(Product)' creates a new object and sets its prototype to产品. You can verify that Object.getPrototypeOf(Object.create(Product))== Product`。
  2. 然后将Food.prototype设置为此新对象。
  3. 现在,当您致电var cheese = new Food('feta', 5)时,您正在调用Food作为构造函数。因此,创建了一个新对象,并将Food.prototype设置为其原型,即新对象的原型是一个原型为Product的对象。
  4. 接下来,使用此新对象作为其Food参数调用this。然后Foodthis参数传递给Product。到目前为止,这么好,对吗?
  5. 这是捕获。你会发现Food.constructor == Function。咦? Product发生了什么事?好吧,你的原型链回到Product,肯定,Product的原型是Function.prototype,并且该链上的任何地方都没有覆盖构造函数。
  6. 所以...以某种奇怪的方式,它看起来好像cheese是一个函数?好吧,它不是(例如你不能调用它),但是它不会继承你放在Product.prototype中的任何属性或方法(虽然它会继承Product中放置的属性或方法)。不好的是它还将继承Function中定义的任何方法 - 例如,call方法本身。更糟糕的是,尝试使用它(例如cheese.call({}))会引发错误,因为cheese本身不是一个函数。

    因此,正确的方法是使用Food.prototype = Object.create(Product.prototype)(或等效地,Food.prototype = new Product)。

    这里的关键点可能是Product.prototype不是Product的原型。相反,它是通过调用new Product(...)创建的新对象的原型。

答案 1 :(得分:0)

无论哪种方式都有效。两个不同的对象可以具有相同的原型。您可以将一个原型(例如Foo.prototype)设置为与Bar.prototype相同的对象。因此,您可以将其设置为对象的原型或对象的实例。你可以做任何一个。

不同之处在于,有时你想让一个对象Foo从另一个对象Bar继承方法,所以你让Foo.prototype等于Bar.prototype。因此,Bar原型上的每个方法现在都可以作为Foo上的方法使用。但是,让我们想要将方法添加到Bar的原型中,而不是添加到Foo的。在这种情况下,你可以按照你的建议做,并使Bar的原型成为Foo对象的一个​​实例而不是它的原型。这样,当你将方法添加到Bar的原型时,你只是将它添加到Foo的一个实例,而不是影响所有Foo实例的Foo原型。