什么是原型链(Prototype Chain)?proto和prototype的关系与区别是什么?
一、定义
原型链是 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 调用对象的「自己没有的属性 / 方法」(最常用)
// 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 个学生都要 “上课”),不用给每个对象都写一遍(浪费内存),把方法放在它们的 “共同上级”(原型对象)里,原型链会帮所有对象找到这个方法。
// 1. 定义“学生的共同上级”(原型对象),存一个共用方法“上课” const 学生原型 = { 上课: function() { console.log(`${this.name}去上课啦!`); } }; // 2. 造2个学生,让它们的“上级”都是「学生原型」(用Object.create连原型链) const 小明 = Object.create(学生原型); 小明.name = "小明"; const 小红 = Object.create(学生原型); 小红.name = "小红"; // 3. 两个学生都没自己写“上课”方法,但都能调用(原型链找的) 小明.上课(); // 小明去上课啦! 小红.上课(); // 小红去上课啦! // 关键:两个学生共用同一个“上课”方法,只存了一次,省内存! console.log(小明.上课 === 小红.上课); // true4.3 实现【继承】(子类用父类的功能)
比如你想写一个 “小学生” 类,小学生既要能 “上课”(继承学生的功能),还要能 “写作业”(自己的功能)—— 原型链就是 JS 实现这种 “继承” 的核心。
// 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(同时也是学生实例)