原型链是 JavaScript 实现继承的核心机制,本质是一条「实例与原型之间的引用链条」,用于解决属性和方法的查找、共享与继承问题,理解原型链是掌握 JavaScript 面向对象编程的关键。
一、先搞懂 3 个核心概念(原型链的基础)
在讲原型链之前,必须先明确 prototype、__proto__、constructor 这三个不可分割的概念,它们是构成原型链的基本单元。
1. prototype(原型属性 / 显式原型)
-
定义 :只有函数(构造函数)才拥有
prototype属性,它指向一个对象(称为「原型对象」),这个对象的作用是存放所有实例需要共享的属性和方法。 -
通俗理解:构造函数的「原型仓库」,所有通过该构造函数创建的实例,都能共享这个仓库里的内容,避免方法重复创建浪费内存。
-
示例:
js// 构造函数 function Person(name) { this.name = name; // 实例私有属性 } // prototype 指向原型对象,存放共享方法 Person.prototype.sayName = function() { console.log('我的名字:', this.name); }; console.log(Person.prototype); // { sayName: ƒ, constructor: ƒ Person() }
2. __proto__(原型链指针 / 隐式原型)
-
定义 :几乎所有对象(除
null/undefined)都拥有__proto__属性(ES6 规范中称为[[Prototype]],__proto__是浏览器提供的访问接口),它指向 创建该对象的构造函数的原型对象(prototype) 。 -
通俗理解:对象的「原型导航器」,通过它可以找到自己的 "原型仓库",进而向上查找属性 / 方法。
-
示例:
jsconst person1 = new Person('张三'); // person1 的 __proto__ 指向 Person.prototype console.log(person1.__proto__ === Person.prototype); // true console.log(person1.__proto__.sayName === Person.prototype.sayName); // true
3. constructor(构造函数指向)
-
定义 :原型对象(
prototype)中默认包含constructor属性,它指向对应的构造函数本身,用于标识对象的创建来源。 -
作用:修复原型指向后,保证实例能正确追溯到构造函数(避免继承时构造函数指向混乱)。
-
示例:
js// 原型对象的 constructor 指向构造函数 console.log(Person.prototype.constructor === Person); // true // 实例可通过 __proto__ 找到 constructor console.log(person1.__proto__.constructor === Person); // true console.log(person1.constructor === Person); // true(自动向上查找)
二、原型链的核心定义与形成过程
1. 核心定义
原型链是由 __proto__ 串联起来的「对象 → 原型对象 → 上层原型对象 → ... → null」的链式结构,当访问一个对象的属性 / 方法时,JavaScript 会先在对象自身查找,找不到则通过 __proto__ 向上查找原型对象,依次类推,直到找到属性 / 方法或到达原型链末端(null)。
2. 原型链的形成过程(三步成型)
我们以 Person 实例为例,拆解原型链的形成:
- 第一步 :创建构造函数
Person,其prototype指向Person原型对象(包含sayName方法和constructor); - 第二步 :通过
new Person()创建实例person1,person1.__proto__指向Person.prototype(形成第一层链接); - 第三步 :
Person.prototype是一个普通对象,它的__proto__指向Object.prototype(JavaScript 所有对象的根原型),Object.prototype.__proto__指向null(原型链末端)。
最终形成的原型链:
plaintext
js
person1(实例)
↓ __proto__
Person.prototype(Person 原型对象)
↓ __proto__
Object.prototype(根原型对象)
↓ __proto__
null(原型链末端)
可视化示例
js
// 验证原型链结构
const person1 = new Person('张三');
// 第一层:person1 -> Person.prototype
console.log(person1.__proto__ === Person.prototype); // true
// 第二层:Person.prototype -> Object.prototype
console.log(Person.prototype.__proto__ === Object.prototype); // true
// 第三层:Object.prototype -> null
console.log(Object.prototype.__proto__ === null); // true
// 完整原型链:person1 -> Person.prototype -> Object.prototype -> null
三、原型链的核心作用:属性 / 方法查找机制
这是原型链最核心的功能,遵循「自身优先,向上追溯,末端终止」的规则:
1. 查找规则步骤
- 当访问对象的某个属性 / 方法时,先在对象自身 的属性中查找(比如
person1.name,直接在person1上找到); - 如果自身没有,就通过
__proto__向上查找原型对象 (比如person1.sayName(),自身没有,找到Person.prototype上的sayName); - 如果原型对象也没有,继续通过原型对象的
__proto__向上查找上层原型 (比如person1.toString(),Person.prototype没有,找到Object.prototype上的toString); - 直到找到目标属性 / 方法,或到达原型链末端
null,此时返回undefined(属性)或报错(方法)。
2. 代码示例
js
const person1 = new Person('张三');
// 1. 查找自身属性:name
console.log(person1.name); // 张三(自身存在,直接返回)
// 2. 查找原型方法:sayName
console.log(person1.sayName()); // 我的名字:张三(自身没有,向上找到 Person.prototype)
// 3. 查找上层原型方法:toString
console.log(person1.toString()); // [object Object](Person.prototype 没有,向上找到 Object.prototype)
// 4. 查找不存在的属性:age
console.log(person1.age); // undefined(原型链末端仍未找到,返回 undefined)
3. 注意:属性修改仅影响自身,不影响原型
原型链是「只读」的查找链路,修改对象的属性时,只会修改对象自身,不会改变原型对象的属性(除非直接显式修改原型):
js
// 错误:试图修改原型方法(实际是给 person1 新增了一个私有方法 sayName,覆盖了原型查找)
person1.sayName = function() {
console.log('我是私有方法:', this.name);
};
person1.sayName(); // 我是私有方法:张三(优先访问自身方法)
console.log(Person.prototype.sayName()); // 我的名字:undefined(原型方法未被修改)
四、原型链与继承的关系
原型链是 JavaScript 继承的底层支撑 ,所有继承方式(原型链继承、组合继承等)本质都是通过修改 __proto__ 或 prototype,构建新的原型链结构,实现子类对父类属性 / 方法的继承。
示例:简单继承的原型链结构
js
// 父类构造函数
function Animal(name) {
this.name = name;
}
Animal.prototype.sayName = function() {
console.log('名称:', this.name);
};
// 子类构造函数
function Dog(name, breed) {
Animal.call(this, name);
this.breed = breed;
}
// 构建继承:让 Dog.prototype.__proto__ 指向 Animal.prototype
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
// 子类实例的原型链
const dog1 = new Dog('旺财', '中华田园犬');
// 原型链:dog1 -> Dog.prototype -> Animal.prototype -> Object.prototype -> null
console.log(dog1.__proto__ === Dog.prototype); // true
console.log(Dog.prototype.__proto__ === Animal.prototype); // true
console.log(Animal.prototype.__proto__ === Object.prototype); // true
// 继承生效:dog1 能访问 Animal.prototype 的 sayName 方法
dog1.sayName(); // 名称:旺财
五、原型链的末端:Object.prototype 与 null
-
Object.prototype:是 JavaScript 所有对象的「根原型」,所有对象最终都会继承它的属性和方法(如toString()、hasOwnProperty()、valueOf()等); -
null:是原型链的「终点」,Object.prototype.__proto__指向null,表示没有上层原型,查找过程到此终止; -
验证:
jsconsole.log(Object.prototype.__proto__); // null console.log(Object.prototype.hasOwnProperty('toString')); // true(根原型的自有方法) console.log(person1.hasOwnProperty('name')); // true(自身属性) console.log(person1.hasOwnProperty('sayName')); // false(原型上的方法,非自身属性)
六、常见误区
- 混淆
prototype和__proto__:prototype是函数的属性,__proto__是对象的属性,两者的关联是「对象.proto = 构造函数.prototype」; - 原型链是可写的 :
__proto__可以手动修改(不推荐,会破坏原有继承结构,影响性能); - 所有对象都有
prototype:只有函数才有prototype,普通对象只有__proto__; hasOwnProperty能查找原型属性 :hasOwnProperty仅判断对象自身是否有该属性,不会向上查找原型链。
总结
- 原型链的核心是
__proto__串联的链式结构,末端是null,根节点是Object.prototype; - 3 个核心概念:
prototype(函数的原型仓库)、__proto__(对象的原型指针)、constructor(原型的构造函数指向); - 核心功能:实现属性 / 方法的分层查找(自身 → 原型 → 上层原型 → ... →
null),支撑 JavaScript 继承机制; - 本质:通过共享原型对象的属性 / 方法,实现代码复用,减少内存消耗。