JavaScript 原型链与属性查找:从懵圈到豁然开朗
原型链是 JavaScript 的核心概念之一,也是很多初学者容易混淆的地方。本文将用最直白的语言和生动的比喻,带你彻底理解 JS 的原型链机制和属性查找规则。
作为 JavaScript 初学者,你一定遇到过这样的困惑:为什么一个空的对象 {},竟然可以直接调用 .toString() 方法?这背后其实隐藏着 JS 的一个重要机制 —— 原型链(Prototype Chain) 。
今天,我们就来揭开它的神秘面纱!
什么是原型链?
简单来说,每个 JavaScript 对象都有一个内部链接,指向另一个对象,这个对象被称为它的 原型(Prototype) 。当我们试图访问对象的一个属性时,如果对象本身没有这个属性,JavaScript 引擎就会去它的原型上查找。如果原型上也没有,就再往上找原型的原型…… 这样层层递进,形成了一条“链条”,这就是所谓的“原型链”。
核心规则:原型链只在“读取”时生效
这是理解原型链的关键点:原型链的查找机制,仅在你尝试获取(读取)一个对象的属性值时才会被触发。
const obj = {};
// 1. 读取操作,会触发原型链查找
console.log(obj.toString); // 找到了,来自 Object.prototype
console.log(obj.someNonExistentProperty); // 找不到,返回 undefined
// 2. 写入操作,不会触发原型链查找
obj.newProp = 'I am new!';
// 此时 obj 自己就拥有了 newProp 属性,和原型无关
属性查找的详细流程
当你执行 obj.someProperty 时,JavaScript 引擎会按以下步骤进行查找:
- 检查对象自身:首先检查
obj自身有没有名为someProperty的属性。 - 查找原型:如果没有,就去看
obj的原型(即obj.__proto__或Object.getPrototypeOf(obj))上有没有这个属性。 - 继续向上:如果原型上也没有,就再看原型的原型(即
Object.getPrototypeOf(原型))上有没有。 - 直到终点:这个过程会一直持续,直到查找到
Object.prototype为止(它是所有对象的“祖宗”,其__proto__值为null)。 - 返回结果:如果整条链都找完了还是没找到,表达式的结果就是
undefined。
实例演示:一步步看查找过程
让我们通过一个具体的例子来加深理解:
// 1. 定义一个构造函数
function Person(name) {
this.name = name; // 实例自身的属性
}
// 2. 给构造函数的原型添加方法
Person.prototype.sayHello = function() {
console.log(`Hello, I'm ${this.name}`);
};
// 3. 创建一个实例
const alice = new Person("Alice");
// 4. 测试属性查找
console.log(alice.name); // "Alice" - 来自实例自身
console.log(alice.sayHello); // function - 来自 Person.prototype
alice.sayHello(); // "Hello, I'm Alice"
// 5. 访问一个实例和原型都没有的属性
console.log(alice.age); // undefined - 整个原型链都找不到
// 6. 访问 Object.prototype 上的方法
console.log(alice.toString); // function - 来自 Object.prototype
查找过程解析:
alice.name:alice自己有name,直接返回"Alice"。alice.sayHello:alice自己没有sayHello,于是查找alice.__proto__(即Person.prototype),找到了,返回函数。alice.age:alice没有,Person.prototype也没有,继续查Object.prototype,还是没有,返回undefined。alice.toString:alice没有,Person.prototype也没有,但Object.prototype上有toString方法,因此成功调用。
类比理解(助记)
想象一下你在图书馆找一本专业书籍:
- 先查自己的书架(对象自身):看看自己有没有收藏。
- 问室友(原型):没有的话,问室友借过没。
- 问班长(原型的原型):室友也没有,就去问班长。
- 问图书管理员(
Object.prototype):班长也不知道,最后只能求助图书管理员。 - 找不到(返回
undefined):管理员说图书馆没有这本书,那你就借不到了。
总结与延伸
-
原型链的核心:是一种实现对象间属性共享和继承的机制。
-
属性查找规则:只发生在读取操作时,遵循“就近原则”,逐级向上查找。
-
终点:所有普通对象的原型链最终都会指向
Object.prototype。 -
判断自有属性:可以用
hasOwnProperty()方法判断一个属性是否属于对象自身,而非继承而来。console.log(alice.hasOwnProperty('name')); // true console.log(alice.hasOwnProperty('sayHello')); // false -
检测属性存在性:
in操作符会检查整个原型链。console.log('toString' in alice); // true console.log('age' in alice); // false
理解了原型链,你对 JavaScript 的对象模型和继承机制就有了更深一层的认识。这是进阶 JS 必须掌握的基础知识!