JavaScript 中的原型 (Prototype) 和原型链 (Prototype Chain),是 JavaScript 面向对象编程的基石,也是它与其他基于类的语言(如 Java、C++)最大的不同之处。
文章目录
1. 核心概念:为什么需要原型?
JavaScript 在诞生之初,为了简化语言,被设计为一种基于原型 (Prototype) 而不是类 (Class) 的语言。它的继承机制是通过对象之间的关联来实现的,而不是通过类复制。
每个 JavaScript 对象(除 null 外)都有一个隐藏属性 [[Prototype]](在 ES5 标准中可通过 __proto__ 访问),它指向另一个对象,这个对象就是它的原型。
核心目的:实现属性和方法的共享,节省内存,实现继承。
2. 理解 __proto__ 和 prototype
这是最容易混淆的两个概念。记住它们的区别:
| 特性 | __proto__ | prototype |
|---|---|---|
| 所有者 | 每一个对象都拥有 | 只有函数对象才拥有 (ES6+ 的类也是函数) |
| 含义 | 指向该对象的原型对象 (谁是我的父亲?) | 作为构造函数时,它创建的实例的 __proto__ 将指向它 (我的孩子应该以谁为原型?) |
| 标准 | 非标准属性,但被大多数浏览器实现。ES6 中将其标准化,但推荐使用 Object.getPrototypeOf() | 标准属性 |
简单关系公式:
// 当一个函数 Func 被作为构造函数(通过 new 关键字)调用时
instance = new Func();
// 会有以下关系成立:
instance.__proto__ === Func.prototype // true
3. 图解原型链
让我们通过一个最简单的例子来构建原型链。
// 1. 创建一个构造函数
function Person(name) {
this.name = name;
}
// 2. 在构造函数的 prototype 上添加方法
Person.prototype.sayHello = function() {
console.log(`Hello, my name is ${this.name}`);
};
// 3. 使用 new 创建实例
const alice = new Person('Alice');
const bob = new Person('Bob');
// 4. 访问属性和方法
alice.sayHello(); // "Hello, my name is Alice"
bob.sayHello(); // "Hello, my name is Bob"
// 验证关系:
console.log(alice.__proto__ === Person.prototype); // true
console.log(bob.__proto__ === Person.prototype); // true
// Person.prototype 本身也是一个对象,它也有 __proto__
console.log(Person.prototype.__proto__ === Object.prototype); // true
// Object.prototype 是绝大多数对象的原型链终点
console.log(Object.prototype.__proto__); // null
此时的完整原型链如下图所示:

查找机制(原型链的工作原理):
当访问 alice.sayHello() 时,JavaScript 引擎会:
- 首先在
alice对象自身查找是否有sayHello方法。 - 如果没有,就通过
alice.__proto__(也就是Person.prototype)去它的原型上找。 - 在
Person.prototype上找到了,于是调用它。 - 如果还没找到,就继续通过
Person.prototype.__proto__(也就是Object.prototype)往上找。 - 如果在
Object.prototype上也没找到,再往上就是null,查找停止,返回undefined。
这条由 __proto__ 串联起来的链状结构,就是原型链。
4. 完整的原型链
在上面的例子中,原型链是:
alice → Person.prototype → Object.prototype → null
对于数组、函数等内置对象,它们的原型链更长:
const arr = [1, 2, 3];
// arr -> Array.prototype -> Object.prototype -> null
function fn() {}
// fn -> Function.prototype -> Object.prototype -> null
甚至构造函数本身也是函数:
// Person -> Function.prototype -> Object.prototype -> null
console.log(Person.__proto__ === Function.prototype); // true
5. 相关方法与操作
-
获取原型:
Object.getPrototypeOf(obj)console.log(Object.getPrototypeOf(alice) === Person.prototype); // true // (推荐使用这种方式,而不是 obj.__proto__) -
设置原型:
Object.setPrototypeOf(obj, prototype)(性能差,不推荐) 或Object.create()const parent = { familyName: 'Smith' }; const child = Object.create(parent); // 以 parent 对象为原型创建 child console.log(child.familyName); // 'Smith' console.log(Object.getPrototypeOf(child) === parent); // true -
检查自身属性:
obj.hasOwnProperty(prop)console.log(alice.hasOwnProperty('name')); // true (来自构造函数) console.log(alice.hasOwnProperty('sayHello')); // false (来自原型链) -
instanceof运算符
检查构造函数的prototype属性是否出现在实例对象的原型链上。console.log(alice instanceof Person); // true console.log(alice instanceof Object); // true (因为 Object.prototype 在 alice 的原型链上)
6. ES6 中的 Class 语法糖
ES6 引入的 class 本质上是原型继承的语法糖,让写法更清晰、更像传统面向对象语言。
class Person {
constructor(name) {
this.name = name; // 实例属性
}
// 这个方法会自动添加到 Person.prototype 上
sayHello() {
console.log(`Hello, my name is ${this.name}`);
}
// 静态方法,直接存在于 Person 上,实例无法访问
static describe() {
console.log('I am a Person class');
}
}
const alice = new Person('Alice');
alice.sayHello(); // "Hello, my name is Alice"
// 其底层原型关系与之前的函数构造方式完全一致
console.log(alice.__proto__ === Person.prototype); // true
console.log(Person.prototype.hasOwnProperty('sayHello')); // true
总结
| 概念 | 描述 |
|---|---|
| 原型 (Prototype) | 一个对象,用于共享属性和方法。Func.prototype 是构造函数创建实例时的蓝图。 |
| 原型链 (Prototype Chain) | 由 __proto__ 链接起来的对象链,用于实现继承和属性查找。查找规则是自下而上。 |
__proto__ | 对象的一个属性,指向它的原型对象。 |
prototype | 函数的一个属性,指向一个对象,该对象将成为由此函数创建的实例的原型。 |
| 核心思想 | 委托 (Delegation):对象自身没有的属性或方法,会委托给它的原型去查找。 |
理解原型和原型链是理解 JavaScript 对象模型、继承、new 关键字工作原理以及许多现代框架和库的基础。

742

被折叠的 条评论
为什么被折叠?



