一、定义
原型链是 JavaScript 实现继承和属性查找的核心机制,通俗点就是 "对象自己没有某个东西,就一层层向上找别人借" 的链条
- proto:相当于一个向上查找的链条(工具)
- prototype(原型对象):存储公共属性/方法
二、__proto__和prototype的区别
| 对比维度 | __proto__(隐式原型) |
prototype(显式原型) |
|---|---|---|
| 所属主体 | 所有对象(除 null)------ 包括实例对象、原型对象本身 |
仅构造函数(如 Object、Array、自定义构造函数) |
| 核心作用 | 建立 "实例→原型对象" 的关联,作为属性查找的 "指针" | 存储该构造函数所有实例的共享属性 / 方法(相当于实例的 "模板") |
| 标准性与使用 | 非 ES 标准(最初是浏览器私有实现),ES6 后纳入但不推荐直接操作;推荐用 Object.getPrototypeOf(obj) 获取原型、Object.setPrototypeOf(obj, proto) 设置原型 |
ES 标准属性,可直接操作(如给构造函数的 prototype 添加共享方法) |
三、实例.proto===其构造函数.prototype
实例通过 proto 找到构造函数的 prototype,从而访问共享内容。
四、用途
4.1 调用对象的「自己没有的属性 / 方法」时(最常用)
TypeScript
// 1. 你创建一个数组(数组是"对象"的一种)
const arr = [1,2,3];
// 2. 你调用arr.push(4) --- 但你没给arr写过push方法啊!
arr.push(4);
console.log(arr); // [1,2,3,4](调用成功)
arr自己没有push → 沿原型链找arr.proto → 找到Array.prototype(数组的"上级")→ Array.prototype里有push方法
4.2 多个对象「共用同一个功能」时(省内存)
如果多个对象需要同一个方法(比如 100 个学生都要 "上课"),不用给每个对象都写一遍(浪费内存),把方法放在它们的 "共同上级"(原型对象)里,原型链会帮所有对象找到这个方法。
TypeScript
// 1. 定义"学生的共同上级"(原型对象),存一个共用方法"上课"
const 学生原型 = {
上课: function() {
console.log(`${this.name}去上课啦!`);
}
};
// 2. 造2个学生,让它们的"上级"都是「学生原型」(用Object.create连原型链)
const 小明 = Object.create(学生原型);
小明.name = "小明";
const 小红 = Object.create(学生原型);
小红.name = "小红";
// 3. 两个学生都没自己写"上课"方法,但都能调用(原型链找的)
小明.上课(); // 小明去上课啦!
小红.上课(); // 小红去上课啦!
// 关键:两个学生共用同一个"上课"方法,只存了一次,省内存!
console.log(小明.上课 === 小红.上课); // true
4.3 实现【继承】时(子类用父类的功能)
比如你想写一个 "小学生" 类,小学生既要能 "上课"(继承学生的功能),还要能 "写作业"(自己的功能)------ 原型链就是 JS 实现这种 "继承" 的核心。
TypeScript
// 1. 定义父类:学生类(基础类)
function Student(name) {
this.name = name; // 每个学生都有自己的名字
}
// 2. 给学生类的原型添加通用方法(所有学生都会的功能)
Student.prototype.goToClass = function() {
console.log(`${this.name} 去上常规课啦!`);
};
// 3. 定义子类:小学生类(继承学生类)
function Pupil(name, grade) {
// 第一步:继承父类的属性(把父类的构造逻辑应用到子类实例上)
Student.call(this, name);
// 第二步:添加子类独有的属性
this.grade = grade; // 小学生特有:年级
}
// 4. 核心:通过原型链实现方法继承
// 让小学生的原型 指向 学生类的实例(建立原型链关联)
Pupil.prototype = Object.create(Student.prototype);
// 修正构造函数指向(否则Pupil实例的constructor会指向Student)
Pupil.prototype.constructor = Pupil;
// 5. 给小学生类添加独有的方法(子类扩展功能)
Pupil.prototype.doHomework = function() {
console.log(`${this.name}(${this.grade}年级)开始写小学生作业啦!`);
};
// 6. 测试效果
const xiaoMing = new Pupil("小明", 3);
// 继承父类的方法
xiaoMing.goToClass(); // 输出:小明 去上常规课啦!
// 子类自己的方法
xiaoMing.doHomework(); // 输出:小明(3年级)开始写小学生作业啦!
// 验证原型链关系
console.log(xiaoMing instanceof Pupil); // true(是小学生实例)
console.log(xiaoMing instanceof Student); // true(同时也是学生实例)