使用object.create()设置函数的原型

问题描述 投票:0回答:2

我正在看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)

这是否与它的功能有关?

谢谢,

javascript inheritance prototype
2个回答
1
投票

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

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

Product本身就是一个对象 - 它是一个函数,函数是对象。所有对象都有一个原型,这与你使用Product.prototype获得的原型不同 - 你可以使用Object.getPrototypeOf(Product)访问它,你会发现它等于Function.prototype。所有对象也有一个构造函数,可以使用Product.constructor获得。由于产品是一个函数,它的构造函数是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 toProduct. You can verify thatObject.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,并且沿着那条链的任何地方都没有被覆盖的构造函数。

所以...以某种奇怪的方式,它看起来好像cheese是一个函数?好吧,它不是(你不能称之为,例如),但它不会继承你放在Product.prototype中的任何属性或方法(尽管它会继承放在Product中的那些)。不好的是它还将继承Function中定义的任何方法 - 例如,call方法本身。更糟糕的是,尝试使用它(例如cheese.call({}))会引发错误,因为cheese本身不是一个函数。

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


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


0
投票

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

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

© www.soinside.com 2019 - 2024. All rights reserved.