我是否正确理解 JavaScript 如何查找属性?
本质上,是的。但需要注意的是,对象的底层原型是由new
操作设置为指向构造函数的prototype
属性所引用的对象,此时如果将构造函数的属性指向prototype
完全不同的对象,它不会有任何对现有孩子的影响。孩子指的是对象,而不是属性。
所以一般来说,属性查找是这样工作的:
- 该属性是否存在于对象本身上?如果是这样,请使用它的值。
- 不,该属性是否存在于对象的底层原型上?如果是这样,请使用它的值。
- 它是否存在于其原型的原型上?如果是这样,请使用该值。
- 依此类推,直到我们找到属性,或者原型用完。
让我们在你正在构建的东西上扔一些 ASCII-Art,只是为了好玩:
+-----------+
| 家长 |<----------------------------+
+-----------+ |
| 原型 |--------->+-------------+ |
+-----------+ +--->| (对象) | |
| +->+--------------+ |
| | | 构造函数 |--+ +------------------+
| | | 说|--------->| (函数) |
| | +--------------+ +------------------+
| | | 警报(“父母”);|
| | +------------------+
| |
| +-------------------------------------------------- -+
| |
+------------------------+ |
+-----------+ | |
| 孩子 |<----------------------------+ | |
+------------+ +-------------+ | | |
| 原型 |--------+--->| (对象) | | | |
+-----------+ | +--------------+ | | |
| | 构造函数 |--+ | |
| | __proto__ |----+ +-----------------+ |
| | 说|--------->| (函数) | |
| +-------------+ +------------------+ |
| | 警报(“孩子”);| |
+-----------+ | +------------------+ |
| c | | |
+-----------+ | |
| __proto__ |------+ |
+-----------+ |
|
+-----------+ |
| p | |
+-----------+ |
| __proto__ |------------------------------------------------ ------------+
+-----------+
...__proto__
表示对象到其原型的链接的隐藏属性在哪里。(一些引擎实际上公开了这一点,并且有一个建议将其添加到标准中。)
如您所见,Child.prototype
实例c
都__proto__
指向同一个对象(对于 and 也是类似Parent.prototype
的p
)__proto__
。
我在上面的第一段中做出区分的原因是:
function Foo() {
}
Foo.prototype.a = 1;
var f1 = new Foo();
Foo.prototype = {b: 2}; // Putting an entirely new object on `prototype`, not just modifying it
var f2 = new Foo();
console.log(f1.a); // "1"
console.log(f1.b); // "undefined"
console.log(f2.a); // "undefined"
console.log(f2.b); // "2"
f1
并f2
最终拥有完全不同的原型,到上述结束时,f1
's__proto__
不再指代同一个对象Foo.prototype
。
出于这个原因,除了在非常特殊的情况下(比如你的extend
函数),我强烈建议不要将新对象分配给prototype
构造函数的属性,因为它会变得非常混乱。:-) 你的extend
函数又是一个例外。