我阅读了大量有关原型的材料并了解继承的一般情况。
然而,这是困扰我的一件事,我无法弄清楚。
On dmitrysoshnikov.com http://dmitrysoshnikov.com/ecmascript/javascript-the-core-2nd-edition/#comments有一个简化的示例说明了如何使用以下代码片段实现原型继承:
// Generic prototype for all letters.
let letter = {
getNumber() {
return this.number;
}
};
let a = {number: 1, __proto__: letter};
let b = {number: 2, __proto__: letter};
// ...
let z = {number: 26, __proto__: letter};
console.log(
a.getNumber(), // 1
b.getNumber(), // 2
z.getNumber(), // 26
);
下面是这张图
然而,当我们开始使用实际的继承结构(使用 new 关键字)时,它开始看起来像这样:
我明白它是如何运作的。我不明白的是,为什么我们突然需要所有子实例继承自的 Letter.prototype 对象,而不是像上面的第一个图那样拥有它。对我来说,第一个示例似乎没有任何问题。
我能想到的一个潜在原因是实际方法允许在类中实现静态方法/属性。在上面的示例中,如果您添加静态方法,那么它将是添加到 Letter 对象的函数,而不是添加到 Letter.prototype 对象的函数。子对象 (a,b,z) 将无权访问该函数。
在第一个示例中,这种功能必须以不同的方式实现,但我仍然认为这不是创建新 Prototype 对象的充分理由。我认为这个静态方法功能可以在没有它的情况下实现。
我错过了什么吗?
EDIT:
我认为有很多人试图解释我很感激的事情,但我不确定我的问题为什么 javascript 运行时被设计为以一种方式而不是另一种方式运行正确理解。
为了表明我的意思,我尝试了一些事情。
class Car{
method() {
console.log("hello")
}
}
myCar = new Car();
// First a few tests as expected
myCar.method() // works
console.log(myCar.method === Car.method) // False, JS doesn't work that way, ok...
console.log(myCar.method === Car.prototype.method) // This is how it works, fine...
// How about we move the reference to the method up one level
Car.method = Car.prototype.method
// Delete the reference to it in prototype object,
// Btw. I tried to remove reference to whole prototype but somehow doesn't let me
delete Car.prototype.method
// Change the prototype chain so it links directly to Car and not Car's prototype object
myCar.__proto__ = Car
myCar.method() // Still works!!!
console.log(myCar.method === Car.method) // True !
console.log(myCar.method === Car.prototype.method) // False, we deleted the method property out of Car.prototype
So, Car.prototype
不再需要,至少对于 myCar 的执行来说不再需要。
那么为什么该方法会进入到里面呢Car.prototype
并不是Car
那为什么不呢myCar.__proto__ = Car
代替myCar.__proto__ = Car.prototype
?
我不明白为什么我们突然需要Letter.prototype
所有子实例都继承自该对象,而不是像上面的第一个图那样拥有它。
实际上那里什么都没有改变。它仍然是同一个对象,与名为的对象具有相同的用途const letter
在第一个例子中。字母实例继承自它,它存储getNumber
方法,它继承自Object.prototype
.
改变的是额外的Letter
功能。
对我来说,第一个例子似乎没有什么问题。
是的:{number: 2, __proto__: letter}
是一种非常丑陋的创建实例的方式,并且在必须执行更复杂的逻辑来初始化属性时不起作用。
解决这个问题的方法是
// Generic prototype for all letters.
const letterPrototype = {
getNumber() {
return this.number;
}
};
const makeLetter = (number) => {
const letter = Object.create(letterPrototype); // {__proto__: letterPrototype}
if (number < 0) throw new RangeError("letters must be numbered positive"); // or something
letter.number = number;
return letter;
}
let a = makeLetter(1);
let b = makeLetter(2);
// ...
let z = makeLetter(26);
console.log(
a.getNumber(), // 1
b.getNumber(), // 2
z.getNumber(), // 26
);
现在我们有两个值,makeLetter
and letterPrototype
某种程度上属于彼此。另外,在比较各种make…
函数,它们都共享相同的模式,首先创建一个继承各自原型的新对象,然后在最后返回它。为了简化,引入了通用构造:
// generic object instantiation
const makeNew = (prototype, ...args) => {
const obj = Object.create(prototype);
obj.constructor(...args);
return obj;
}
// prototype for all letters.
const letter = {
constructor(number) {
if (number < 0) throw new RangeError("letters must be numbered positive"); // or something
letter.number = number;
},
getNumber() {
return this.number;
}
};
let a = makeNew(letter, 1);
let b = makeNew(letter, 2);
// ...
let z = makeNew(letter, 26);
console.log(
a.getNumber(), // 1
b.getNumber(), // 2
z.getNumber(), // 26
);
你能看到我们要去哪里吗?makeNew
实际上是语言的一部分new
操作员。虽然这可行,但实际选择的语法是使constructor
传递给的值new
和存储的原型对象.prototype
的构造函数。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)