【问题标题】:setting the prototype of a function using object.create()使用 object.create() 设置函数的原型
【发布时间】:2015-07-02 03:33:31
【问题描述】:

我正在查看来自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 的原型现在不就是 Product 的原型了吗?即 Function.prototype?

我期待:

Food.prototype = Object.create(Product)

这是否与它是一个函数这一事实有关?

谢谢,

【问题讨论】:

  • "i.e Function.prototype?" prototype 的属性function 最初是一个普通的Object。该属性用于继承而不是 function 本身,因此各种类型不必继承 Function 行为。
  • Constructor.prototype 是对应该由Constructor 的实例继承的对象的引用,而obj.prototype 只是一个常规属性,就像任何其他属性一样,例如a.prototype 和@987654334 @ 在你的代码中都是 undefined.

标签: javascript inheritance prototype


【解决方案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.prototypeProduct.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 参数。 Food 然后将this 参数传递给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(...) 创建的新对象的原型。

【讨论】:

  • 非常感谢您的回答;我认为它澄清了很多——尤其是关于 .prototype 属性与如何使用它所属的函数创建新对象有关的部分。不过,我确实有几个问题:在 5) 中,当你说 product 的原型是 Function 时,你的意思是说它是 Function.prototype 吗?另外,既然Product.prototype 是空的,它有什么用呢?链现在看起来像:Food ---&gt; Product.prototype ---&gt; Object.prototype ---&gt; null?
  • 是的,Product的原型是Function.prototypeFood.prototype的原型是Product.prototype,例子中为空;但是,向Product.prototype 添加一些内容也会使其出现在Food.prototype 中,即使它是在很久以后添加的。
  • 还有一件事:我递归调用 Object.getPrototypeOf 来查看链的样子。令我惊讶的是,我总共调用了 4 次才能达到null。这是为什么?我以为链条看起来像: cheese ---> {} ---> Object.prototype ---> null 那只有 3,链条中缺少的环节是什么?当我使用 safari 时,我看到 Product 显示为奶酪的原型。是不是因为它是Product.prototype的构造函数?
  • 关于第一个问题,链条是奶酪 --> Food.prototype --> Product.prototype --> Object.prototype --> null。关于第二个,我认为这只是 Safari 如何打印对象的一个​​工件,通过使用构造函数名称,就好像它是一个类名一样。如果您添加一行Food.prototype.contructor = Food,Safari 将在您请求Object.getPrototypeOf(cheese) 时打印Food。它实际上是打印Food.prototype,您可以通过将一些数据成员添加到Food.prototype 并单击Object.getPrototypeOf(cheese) 结果旁边的显示三角形来验证。
【解决方案2】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-02-09
    • 2012-06-29
    • 2012-07-27
    • 2016-07-10
    • 2021-06-08
    • 2016-12-04
    相关资源
    最近更新 更多