一、前言:为什么原型链如此重要?
在 JavaScript 的世界里,原型链是一个"既熟悉又陌生"的概念。很多开发者知道它的存在,却很少直接操作它。然而,原型链是 JavaScript 面向对象编程的基石,理解它不仅能帮助你通过面试,更能让你深入理解 JavaScript 的设计哲学。
二、核心概念:什么是原型链?
1. 基本定义
原型链是 JavaScript 中实现继承和共享属性的机制。每个对象都有一个内部链接指向它的原型(通过 __proto__ 或 [[Prototype]]),当访问对象的属性时,如果对象自身没有该属性,就会沿着原型链向上查找,直到找到属性或到达链的末端(null)。
2. 关键术语澄清
function Person() {}
const p = new Person();
console.log(p.__proto__);
console.log(Person.prototype);
console.log(Person.__proto__);
3. 原型链的"终点站"
任意对象 → Object.prototype → null ↑ └── 原型链查找的终点
三、原型链的工作原理
1. 属性查找过程
const obj = { name: '小明' };
2. 完整的继承体系
function Animal(name) {
this.name = name;
}
Animal.prototype.eat = function() {
console.log(`${this.name}正在吃`);
};
function Dog(name, breed) {
Animal.call(this, name);
this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
console.log(`${this.name}在叫:汪汪!`);
};
const myDog = new Dog('旺财', '柯基');
四、现代 JavaScript 中的原型链
1. ES6 Class 是语法糖
class Person {
constructor(name) {
this.name = name;
}
sayHello() {
console.log(`Hello, ${this.name}`);
}
}
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log(`Hello, ${this.name}`);
};
const p1 = new Person('Alice');
console.log(p1.sayHello === Person.prototype.sayHello);
2. 继承的实现
class Animal {
constructor(name) {
this.name = name;
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
}
function _inherits(subClass, superClass) {
subClass.prototype = Object.create(
superClass && superClass.prototype,
{
constructor: {
value: subClass
}
}
);
if (superClass) {
Object.setPrototypeOf
? Object.setPrototypeOf(subClass, superClass)
: (subClass.__proto__ = superClass);
}
}
五、原型链在实际开发中的应用
应用 1:框架中的原型扩展
Vue.prototype.$nextTick = function(fn) {
return nextTick(fn, this);
};
Vue.prototype.$set = set;
Vue.prototype.$delete = del;
export default {
mounted() {
this.$nextTick(() => {
});
}
};
应用 2:工具库的实现
function _() {
if (!(this instanceof _)) return new _( ...arguments);
this.__wrapped__ = arguments[0];
}
_.prototype.chain = function() {
this.__chain__ = true;
return this;
};
_.prototype.value = function() {
return this.__wrapped__;
};
应用 3:Polyfill 实现
if (!Array.prototype.includes) {
Object.defineProperty(Array.prototype, 'includes', {
value: function(searchElement, fromIndex) {
},
writable: true,
configurable: true
});
}
六、性能优化:为什么原型链重要?
1. 内存优化对比
function Person(name) {
this.name = name;
this.sayHello = function() {
console.log(`Hello, ${this.name}`);
};
}
const people1 = [];
for (let i = 0; i < 1000; i++) {
people1.push(new Person(`Person${i}`));
}
function BetterPerson(name) {
this.name = name;
}
BetterPerson.prototype.sayHello = function() {
console.log(`Hello, ${this.name}`);
};
const people2 = [];
for (let i = 0; i < 1000; i++) {
people2.push(new BetterPerson(`Person${i}`));
}
2. 属性查找性能
class WithPrototype {
calculate() {
return this.x + this.y;
}
}
class WithClosure {
constructor(x, y) {
this.calculate = () => x + y;
}
}
七、面试常见问题与回答
Q1:new 操作符做了什么?
A:
function myNew(Constructor, ...args) {
const obj = Object.create(Constructor.prototype);
const result = Constructor.apply(obj, args);
return result instanceof Object ? result : obj;
}
Q2:如何实现继承?
A:
function Parent(name) {
this.name = name;
}
Parent.prototype.sayName = function() {
console.log(this.name);
};
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
Child.prototype.sayAge = function() {
console.log(this.age);
};
Q3:Object.create 和 new 的区别?
A:
const proto = { x: 10 };
const obj1 = Object.create(proto);
function F() {}
const obj2 = new F();
const pureObj = Object.create(null);
console.log(pureObj.toString);
Q4:ES6 Class 和 ES5 原型的区别?
A:
- 语法糖:Class 是更清晰的语法,底层仍是原型
- 继承实现:
extends 自动处理原型链
- 静态方法:Class 有明确的 static 语法
- 私有字段:ES2022 支持真正的私有字段
#field
- super 关键字:更优雅地调用父类方法
八、最佳实践与注意事项
✅ 推荐做法
class User {
constructor(name) {
this.name = name;
}
sayHello() {
return `Hello, ${this.name}`;
}
static createAdmin() {
return new User('admin');
}
}
class MyArray extends Array {
myMethod() {
}
}
const canEat = {
eat() {
console.log(`${this.name} is eating`);
}
};
const canSleep = {
sleep() {
console.log(`${this.name} is sleeping`);
}
};
class Animal {
constructor(name) {
this.name = name;
}
}
.(., canEat, canSleep);
⚠️ 注意事项
- 原型链不宜过深(通常不超过 3 层)
- 避免修改
__proto__(使用 Object.setPrototypeOf)
- 注意性能:深层原型链查找会影响性能
- 使用
Object.hasOwnProperty 区分自身属性和继承属性
九、总结:原型链的现代意义
原型链不是过时的概念,而是 JavaScript 核心特性。虽然现代开发中直接操作原型链的场景减少,但理解原型链能帮你:
- 深入理解框架:Vue/React 等框架底层都依赖原型
- 编写高效代码:合理使用原型节省内存
- 调试更顺畅:知道属性/方法的来源
- 通过面试:这是必考的基础知识
- 学习新特性:理解 ES6+ 特性的底层实现
记住:你每天写的 array.map()、class extends、this.$router 都在使用原型链。它不是你需要"使用"的工具,而是你需要"理解"的基石。
掌握原型链,你就掌握了 JavaScript 面向对象编程的核心。