我正在研究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)
这是否与它的功能有关?
谢谢,
答案 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)
会怎么样?
Object.create(Product)' creates a new object and sets its prototype to
产品. You can verify that
Object.getPrototypeOf(Object.create(Product))== Product`。Food.prototype
设置为此新对象。var cheese = new Food('feta', 5)
时,您正在调用Food
作为构造函数。因此,创建了一个新对象,并将Food.prototype
设置为其原型,即新对象的原型是一个原型为Product
的对象。 Food
参数调用this
。然后Food
将this
参数传递给Product
。到目前为止,这么好,对吗?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(...)
创建的新对象的原型。
答案 1 :(得分:0)
无论哪种方式都有效。两个不同的对象可以具有相同的原型。您可以将一个原型(例如Foo.prototype
)设置为与Bar.prototype
相同的对象。因此,您可以将其设置为对象的原型或对象的实例。你可以做任何一个。
不同之处在于,有时你想让一个对象Foo从另一个对象Bar继承方法,所以你让Foo.prototype等于Bar.prototype。因此,Bar原型上的每个方法现在都可以作为Foo上的方法使用。但是,让我们想要将方法添加到Bar的原型中,而不是添加到Foo的。在这种情况下,你可以按照你的建议做,并使Bar的原型成为Foo对象的一个实例而不是它的原型。这样,当你将方法添加到Bar的原型时,你只是将它添加到Foo的一个实例,而不是影响所有Foo实例的Foo原型。