在 JavaScript 中,检查对象之间的原型关系是一个常见的需求。本文将探讨几种不同的方法,并分析它们的优缺点,最终给出最佳实践。
1. 问题背景:如何检查对象是否继承自另一个对象?
假设我们有两个对象:
javascript
var a = {};
var b = Object.create(a);
我们想检查 b
是否继承自 a
(即 a
是否在 b
的原型链上)。
2. 一种"荒谬"的实现方式
书中展示了一种利用 instanceof
的实现方式:
javascript
function isRelatedTo(o1, o2) {
function F(){}
F.prototype = o2;
return o1 instanceof F;
}
虽然这种方法能工作,但它存在几个问题:
- 创建了不必要的构造函数
F
- 实现过于复杂,容易让人以为要这样绕弯才能检查原型关系
- 性能不如直接的原型检查
3. 更好的解决方案:isPrototypeOf
JavaScript 提供了更直接的方法:
javascript
//a是否出现在c的[[Prototype]]链中?
a.isPrototypeOf(b); // true
isPrototypeOf
是 Object.prototype
上的方法,专门用于检查对象是否在另一个对象的原型链上。这个方法就不需要使用函数,它直接使用 b 和 a 之间的对象引用来判断它们的关系。换句话说,isRelatedTo
就是isPrototypeOf
。
4. 其他原型检查方式
4.1 Object.getPrototypeOf
javascript
Object.getPrototypeOf(b) === a; // true
这种方法只能检查直接原型,不能检查整个原型链。
4.2 __proto__
属性
__proto__
是一个访问器属性,它暴露了对象的内部 [[Prototype]]
:
javascript
b.__proto__ === a; // true
注意:
__proto__
不是标准属性,但在所有现代浏览器中都实现了- 它实际上是
Object.getPrototypeOf
和Object.setPrototypeOf
的结合 - 更推荐使用标准方法而非
__proto__
.__proto__的实现大致是这样的:
javascript
Object.defineProperty(Object.prototype, "__proto__", {
get: function() {
return Object.getPrototypeOf(this);
},
set: function(o) {
// ES6 中的 setPrototypeOf(..)
Object.setPrototypeOf(this, o);
return o;
}
});
所以比起属性,它更像一个setter/getter。
5. 实现一个健壮的 isRelatedTo
函数
结合以上知识,我们可以写出更好的实现:
javascript
function isRelatedTo(o1, o2) {
// 处理基本类型
if (o2 === null || typeof o2 !== 'object') {
return false;
}
// 使用标准方法
return o2.isPrototypeOf(o1);
// 或者手动遍历原型链:
// let current = o1;
// while (current !== null) {
// if (current === o2) return true;
// current = Object.getPrototypeOf(current);
// }
// return false;
}
6. 性能比较
方法 | 优点 | 缺点 |
---|---|---|
instanceof 迂回法 |
能工作 | 性能差,实现复杂 |
isPrototypeOf |
标准方法,性能好 | 无 |
Object.getPrototypeOf |
标准方法 | 只能检查直接原型 |
__proto__ |
方便 | 非标准,可能被禁用 |
7. 最佳实践
- 优先使用
isPrototypeOf
进行原型链检查 - 需要获取直接原型时使用
Object.getPrototypeOf
- 避免使用
__proto__
,除非有特殊需求 - 永远不要使用文中开头的
instanceof
迂回方案
8. 总结
JavaScript 提供了多种检查原型关系的方法,理解它们的区别和适用场景很重要。isPrototypeOf
是最适合用来检查对象间原型关系的方法,它简洁、高效且语义明确。
"好的代码不是能工作的代码,而是清晰表达意图的代码。" ------ 《Clean Code》