1. 原型(Prototype)
每个对象在 JavaScript 中都有一个内部属性 [[Prototype]],它指向另一个对象,这个对象就是它的 原型(Prototype) 。
- 原型的作用是:实现属性和方法的共享。
- 所有通过构造函数创建的对象,默认会共享该构造函数的
prototype对象上的属性和方法。 - 原型可以看作是一个对象的“模板”或“父级”。
直接访问和修改对象的原型(即其内部的 [[Prototype]] 属性)的方法有两个:
const person = { name: 'rubin' }
// 访问查找
console.log(person.__proto__) // {__defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, __lookupSetter__: ƒ, …}
console.log(Object.getPrototypeOf(person)) // {__defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, __lookupSetter__: ƒ, …}
// 修改
person.__proto__.age = 18
Object.getPrototypeOf(person).sex = 'man'
console.log(person.age) // 18
console.log(person.sex) // man
2. 原型链(Prototype Chain)
当访问一个对象的属性或方法时,如果该对象自身没有这个属性或方法,JavaScript 引擎会去它的原型对象中查找;
如果原型对象也没有,就继续向上查找它的原型对象,直到顶层对象(通常是 Object.prototype),这个过程形成的链条称为 原型链(Prototype Chain) 。查找顺序是沿着原型链逐级进行的,直到找到属性或到达链的末端(即 null)为止。
原型链的作用
- 属性和方法共享
- 作用:允许多个对象实例共享同一份属性和方法,从而节省内存。
- 解释:当你在构造函数的
prototype上定义方法时,所有基于该构造函数创建的对象实例都可以访问这些方法,而不需要每个实例都拥有自己的副本。
- 实现继承
- 作用:支持对象之间的继承关系,使得子类可以从父类那里继承属性和方法。
- 解释:通过设置子类构造函数的
prototype为父类的一个实例,子类就可以继承父类的属性和方法。
- 动态扩展功能
- 作用:可以在运行时动态地向原型添加新的属性或方法,从而使所有基于该原型的对象实例都能立即获得这些新特性。
- 解释:由于所有实例共享同一个原型对象,因此对原型所做的任何更改都会自动反映在所有实例上。
3. 原型和原型链总结对比
| 项目 | 原型(Prototype) | 原型链(Prototype Chain) |
|---|---|---|
| 含义 | 指向一个对象,用于共享属性和方法 | 属性查找路径,由多个原型组成的一条链 |
| 目的 | 实现属性/方法共享 | 实现继承,支持多级对象关系 |
| 特点 | 每个对象都有原型 | 多层原型连接形成查找路径 |
| 关键机制 | 构造函数的 prototype 属性 | 内部属性 [[Prototype]] 的层层指向 |