JavaScript 原型链详解
·
JavaScript 原型链详解
1. 原型链的基本概念
1.1 什么是原型链?
原型链是JavaScript实现继承和共享属性的核心机制。每个对象都有一个内部属性[[Prototype]](可通过__proto__访问),指向它的原型对象。原型对象也有自己的原型,这样层层链接形成原型链。
// 基本示例
const obj = {};
console.log(obj.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null
2. 原型链的构成
2.1 构造函数、原型对象和实例的关系
function Person(name) {
this.name = name;
}
// 原型对象
Person.prototype.sayHello = function() {
console.log(`Hello, I'm ${this.name}`);
};
// 创建实例
const person1 = new Person('Alice');
// 原型链关系
console.log(person1.__proto__ === Person.prototype); // true
console.log(Person.prototype.constructor === Person); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
2.2 完整的原型链图示
实例对象
↓ __proto__
构造函数.prototype
↓ __proto__
Object.prototype
↓ __proto__
null
3. 原型链的查找机制
3.1 属性访问的过程
function Animal(name) {
this.name = name;
}
Animal.prototype.eat = function() {
console.log(`${this.name} is eating`);
};
const cat = new Animal('Tom');
// 属性查找过程
console.log(cat.name); // 1. 查找自身属性 ✓
console.log(cat.eat); // 2. 自身没有 → 查找 Animal.prototype ✓
console.log(cat.toString); // 3. 继续向上 → Object.prototype ✓
console.log(cat.nonExistent); // 4. 直到null → undefined
3.2 属性屏蔽
function Parent() {
this.value = "parent";
}
Parent.prototype.getValue = function() {
return this.value;
};
function Child() {
this.value = "child";
}
Child.prototype = new Parent();
const obj = new Child();
console.log(obj.value); // "child"(自身属性屏蔽原型属性)
console.log(obj.getValue()); // "child"(方法访问的是自身value)
4. 原型继承的实现方式
4.1 原型链继承(经典方式)
// 父类
function Animal(name) {
this.name = name;
this.colors = ['red', 'blue'];
}
Animal.prototype.sayName = function() {
console.log(`My name is ${this.name}`);
};
// 子类
function Dog(name, age) {
Animal.call(this, name); // 继承属性
this.age = age;
}
// 继承方法
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
// 添加子类方法
Dog.prototype.bark = function() {
console.log('Woof!');
};
const dog1 = new Dog('Buddy', 2);
dog1.colors.push('yellow');
const dog2 = new Dog('Max', 3);
console.log(dog2.colors); // ['red', 'blue'] 不受影响
4.2 ES6的class语法(语法糖)
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
speak() {
super.speak(); // 调用父类方法
console.log(`${this.name} barks`);
}
}
const dog = new Dog('Rex', 'German Shepherd');
dog.speak();
5. 原型链的应用场景
5.1 方法共享(节省内存)
function Car(model) {
this.model = model;
this.mileage = 0;
}
// 所有实例共享这些方法
Car.prototype.drive = function(km) {
this.mileage += km;
console.log(`Driven ${km}km, total: ${this.mileage}km`);
};
Car.prototype.getInfo = function() {
return `${this.model}: ${this.mileage}km`;
};
const car1 = new Car('Toyota');
const car2 = new Car('Honda');
// 方法在原型上,不在每个实例中
console.log(car1.drive === car2.drive); // true
5.2 扩展内置对象
// 为数组添加自定义方法
if (!Array.prototype.customMap) {
Array.prototype.customMap = function(callback) {
const result = [];
for (let i = 0; i < this.length; i++) {
result.push(callback(this[i], i, this));
}
return result;
};
}
const arr = [1, 2, 3];
const doubled = arr.customMap(x => x * 2);
console.log(doubled); // [2, 4, 6]
// 注意:小心原型污染,最好使用自己的工具类
5.3 实现混入(Mixin)模式
// 定义多个Mixin
const CanEat = {
eat(food) {
console.log(`${this.name} is eating ${food}`);
}
};
const CanSleep = {
sleep() {
console.log(`${this.name} is sleeping`);
}
};
// 对象混入
function mixin(target, ...sources) {
Object.assign(target, ...sources);
}
function Cat(name) {
this.name = name;
}
mixin(Cat.prototype, CanEat, CanSleep);
const cat = new Cat('Whiskers');
cat.eat('fish');
cat.sleep();
6. 原型链相关方法和属性
6.1 原型相关操作
function Person(name) {
this.name = name;
}
const person = new Person('Alice');
// 1. 获取原型
console.log(Object.getPrototypeOf(person) === Person.prototype); // true
console.log(person.__proto__ === Person.prototype); // true(非标准)
// 2. 设置原型
const newProto = { sayHello: function() { console.log('Hello') } };
Object.setPrototypeOf(person, newProto);
// 3. 创建指定原型的对象
const obj = Object.create(Person.prototype);
// 4. 检查原型链
console.log(person instanceof Person); // true
console.log(Person.prototype.isPrototypeOf(person)); // true
// 5. 检查自身属性 vs 原型链属性
console.log(person.hasOwnProperty('name')); // true
console.log(person.hasOwnProperty('toString')); // false
// 6. 获取所有属性(包括原型链)
for (let key in person) {
console.log(key);
}
7. 原型链的注意事项
7.1 性能考虑
// 避免过长的原型链
function A() {}
function B() {}
function C() {}
function D() {}
// 创建过长的原型链
D.prototype = new C();
C.prototype = new B();
B.prototype = new A();
const d = new D();
// 属性查找会逐级向上,影响性能
console.log(d.someProperty); // 需要查找4层
7.2 原型污染问题
// 问题:修改原型会影响所有实例
function User() {}
const user1 = new User();
const user2 = new User();
User.prototype.greet = function() {
console.log('Hello');
};
// 所有实例都能访问新方法
user1.greet(); // Hello
user2.greet(); // Hello
// 更危险的:修改内置原型
Array.prototype.push = function() {
console.log('Array push is hacked!');
};
const arr = [];
arr.push(1); // 输出:Array push is hacked!
7.3 安全的原型扩展
// 使用组合代替继承
const animalBehaviors = {
eat() { /* ... */ },
sleep() { /* ... */ }
};
function createAnimal(name) {
const animal = { name };
// 将方法复制到实例,而不是放在原型上
Object.assign(animal, animalBehaviors);
return animal;
}
const cat = createAnimal('Cat');
cat.eat();
8. 实际应用案例
8.1 实现一个简单的EventEmitter
function EventEmitter() {
this.events = {};
}
EventEmitter.prototype.on = function(event, listener) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(listener);
};
EventEmitter.prototype.emit = function(event, ...args) {
if (this.events[event]) {
this.events[event].forEach(listener => listener.apply(this, args));
}
};
// 使用
const emitter = new EventEmitter();
emitter.on('message', function(msg) {
console.log('Received:', msg);
});
emitter.emit('message', 'Hello World');
8.2 实现插件系统
function PluginSystem() {
this.plugins = [];
}
PluginSystem.prototype.use = function(plugin) {
this.plugins.push(plugin);
if (plugin.install) {
plugin.install(this);
}
};
PluginSystem.prototype.run = function() {
this.plugins.forEach(plugin => {
if (plugin.run) {
plugin.run(this);
}
});
};
// 插件
const loggerPlugin = {
install(system) {
system.log = function(msg) {
console.log(`[LOG]: ${msg}`);
};
},
run(system) {
system.log('System is running');
}
};
const system = new PluginSystem();
system.use(loggerPlugin);
system.run();
总结
原型链的核心要点:
- 继承机制:通过
__proto__链接实现属性和方法的继承 - 查找顺序:从自身开始,沿原型链向上查找
- 共享特性:原型上的属性和方法被所有实例共享
- 动态性:修改原型会影响所有已存在的实例
最佳实践:
- 使用
Object.create()创建具有指定原型的对象 - 优先使用ES6的class语法
- 避免修改内置对象的原型
- 对于复杂继承,考虑组合模式代替原型继承
- 使用
Object.getPrototypeOf()而不是__proto__
理解原型链是掌握JavaScript面向对象编程的关键,它解释了JavaScript中"一切皆对象"的本质。
更多推荐


所有评论(0)