理解JS原型

61 阅读2分钟

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)为止。

原型链的作用

  1. 属性和方法共享
    • 作用:允许多个对象实例共享同一份属性和方法,从而节省内存。
    • 解释:当你在构造函数的prototype上定义方法时,所有基于该构造函数创建的对象实例都可以访问这些方法,而不需要每个实例都拥有自己的副本。
  2. 实现继承
    • 作用:支持对象之间的继承关系,使得子类可以从父类那里继承属性和方法。
    • 解释:通过设置子类构造函数的prototype为父类的一个实例,子类就可以继承父类的属性和方法。
  3. 动态扩展功能
    • 作用:可以在运行时动态地向原型添加新的属性或方法,从而使所有基于该原型的对象实例都能立即获得这些新特性。
    • 解释:由于所有实例共享同一个原型对象,因此对原型所做的任何更改都会自动反映在所有实例上。

3. 原型和原型链总结对比

项目原型(Prototype)原型链(Prototype Chain)
含义指向一个对象,用于共享属性和方法属性查找路径,由多个原型组成的一条链
目的实现属性/方法共享实现继承,支持多级对象关系
特点每个对象都有原型多层原型连接形成查找路径
关键机制构造函数的 prototype 属性内部属性 [[Prototype]] 的层层指向

4. 原型链图解

f18a1226771ca417e4123260aad8c22.png