原型链
原型链(Prototype Chain)是 JavaScript 实现继承的核心机制,基于**原型对象(Prototype)**实现对象间的属性和方法共享。理解原型链是掌握 JS 面向对象编程的关键。
一、核心概念
1. 三个关键属性
-
prototype
函数特有的属性,指向该函数创建的对象的原型对象。
例:Person.prototype
是所有new Person()
创建的对象的原型。 -
__proto__
每个对象(除Object.prototype
外)都有的隐式属性,指向其原型对象。
例:const p = new Person(); p.__proto__ === Person.prototype
。 -
constructor
原型对象的属性,指向创建该对象的构造函数。
例:Person.prototype.constructor === Person
。
2. 原型链的工作原理
当访问一个对象的属性/方法时,JS 会:
- 先在对象本身查找。
- 若未找到,则通过
__proto__
逐级向上查找原型链。 - 直到找到属性/方法或到达
Object.prototype
(__proto__
为null
)。
二、原型链结构示例
// 构造函数
function Person(name) {
this.name = name;
}
// 原型方法
Person.prototype.sayHello = function() {
return `Hello, ${this.name}`;
};
// 创建实例
const alice = new Person("Alice");
// 原型链结构:
// alice ---> Person.prototype ---> Object.prototype ---> null
- 验证:
alice.__proto__ === Person.prototype; // true
Person.prototype.__proto__ === Object.prototype; // true
Object.prototype.__proto__ === null; // true
三、原型链继承
1. 基本实现
// 父类
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
return `${this.name} makes a sound.`;
};
// 子类
function Dog(name) {
Animal.call(this, name); // 继承实例属性
}
// 设置原型链:Dog.prototype → Animal.prototype
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog; // 修复 constructor
// 子类方法
Dog.prototype.bark = function() {
return `${this.name} barks.`;
};
// 使用
const dog = new Dog("Buddy");
dog.speak(); // "Buddy makes a sound."
dog.bark(); // "Buddy barks."
2. 原型链结构
dog ---> Dog.prototype ---> Animal.prototype ---> Object.prototype ---> null
四、原型链的问题
1. 引用类型共享问题
若原型中包含引用类型(如数组),所有实例会共享该属性:
function Person() {}
Person.prototype.hobbies = [];
const p1 = new Person();
const p2 = new Person();
p1.hobbies.push("reading");
p2.hobbies; // ["reading"](意外共享)
2. 无法向父类构造函数传参
若通过直接修改 prototype
实现继承,无法在创建子类实例时向父类传参。
五、ES6 类与原型链
ES6 的 class
和 extends
是原型链的语法糖:
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return `${this.name} makes a sound.`;
}
}
class Dog extends Animal {
bark() {
return `${this.name} barks.`;
}
}
// 本质与原型链继承相同
const dog = new Dog("Buddy");
dog.__proto__ === Dog.prototype; // true
Dog.prototype.__proto__ === Animal.prototype; // true
六、原型链 vs. 其他继承方式
方式 | 实现原理 | 优点 | 缺点 |
---|---|---|---|
原型链 | 通过 __proto__ 连接多个原型 | 简单,自然实现多态 | 引用类型共享,传参困难 |
构造函数 | 在子类构造函数中调用父类构造 | 实例属性独立,可传参 | 方法无法复用 |
组合继承 | 结合原型链和构造函数 | 集两者优点 | 父类构造函数调用两次 |
寄生组合 | 优化组合继承(减少父类调用) | 现代 JS 推荐方式 | 复杂度较高 |
ES6 类 | 原型链的语法糖 | 语法简洁,支持静态成员 | 本质仍是原型链 |
七、原型链相关方法
1. 原型操作
-
Object.create(proto)
创建一个新对象,指定其原型为proto
。
例:const obj = Object.create(null)
(创建无原型的对象)。 -
Object.setPrototypeOf(obj, proto)
设置对象的原型(谨慎使用,影响性能)。
2. 检查原型关系
-
instanceof
检查对象是否属于某个类(通过原型链判断)。
例:dog instanceof Dog
→true
。 -
Object.getPrototypeOf(obj)
获取对象的原型(等同于obj.__proto__
)。
八、常见面试问题
-
如何实现继承?原型链继承的原理是什么?
- 答:通过
__proto__
连接原型对象,访问属性时逐级向上查找。
- 答:通过
-
instanceof
的工作原理是什么?- 答:检查右边构造函数的
prototype
属性是否存在于左边对象的原型链中。
- 答:检查右边构造函数的
-
如何判断一个属性是对象自身的还是原型链上的?
- 答:使用
obj.hasOwnProperty(key)
(仅检查自身属性)。
- 答:使用
九、总结
- 原型链是 JS 继承的核心机制,通过
__proto__
连接多个原型对象,实现属性和方法的共享。 - ES6 类本质上仍是基于原型链,但提供了更简洁的语法。
- 理解原型链是掌握 JS 高级特性的基础,包括
this
指向、闭包、框架原理等。