继承和原型链

154 阅读2分钟

继承和原型链

含义

当我们在使用 Object 的时候,通常会有一个私有属性( proto )指向另一个名为原型对象( [[Prototype]] )的对象。原型对象也有自己的私有属性,指向另一个原型对象,层层向上直到一个对象的原型为 null。

如果访问某个属性,JavaScript 不仅会在自身去寻找该属性,也会去自身的原型上去寻找,直到找到或者原型对象为null 终止。

用法

当我们创建一个对象的时候,会自动创建一个私有属性指向 Object.prototype。如

let obj = {}

可以看到 obj[[Prototype]] === Object.prototype。

那么如何改变这个默认行为呢,js 为我们提供了以下几种方法,其各有优缺点。

  • 通过对象字面量的形式创建
let obj = {
  __proto__: {
    a: 1
  }
}

可以看到已经在私有属性上添加了a的属性名。

  • 通过 Object.create 创建
const newObj = Object.create({a: 1,b: '100'})
console.log('newObj==',newObj)

  • 通过 Object.setPrototypeOf 进行属性的添加
let obj = {}
    Object.setPrototypeOf(obj,{a: 1})

  • 通过构造器去修改
    function Person(name){
        this.name = name
    }
    const newName = new Person('lhl')
    console.log('newName==',newName)

  • 直接修改
    const obj = {}
    obj.__proto__ = {a: 1}

优缺点

方法优点缺点
字面量方式浏览器支持标准用法,将proto 属性指向非对象的值会被忽略,而非抛出异常,速度比较快,甚至比 Object.create快有些浏览器不兼容
Object.create被现代浏览器支持,还允许使用Object.create(null)创建没有原型的 对象。不支持 IE8 版本。 如果使用了第二个参数,慢对象的初始化可能会成为性能瓶颈。
构造函数方式所有引擎都支持,速度很快,容易被 JIT 优化。
setPrototypeOf/proto 同理被所有现代引擎支持,允许动态修改对象原型,甚至可以将 create创建的 null 对象设置原型性能不佳。 很多 JS 引擎会优化原型,如果动态设置原型会破坏这些优化,可能会导致引擎重新编译代码。

原理

我们可以将对象这一数据类型细化为 object 对象以array以及 function。

作为object

const obj = {}

当使用这种方法创建的时候,js 会自动将私有属性设置到 Object.prototype上。

其实根本原因是 js 帮你做了构造函数的创建,所以上述代码实际上是

const obj = new Object()

作为 array

const arr = []

他本质也是 js 帮你自动调用了 Array 构造函数,所以真正应该为

const arr = new Array()
console.log(Object.getPrototypeOf(arr) === Array.prototype)

作为函数

作为普通函数
function per(){}

他的私有属性__proto__是指向了 Function.prototype.

由于函数本质上也是一个对象,所以她创建的prototype.proto === Object.prototype

作为构造函数
function per(){}
const pera = new per()
console.log(pera.__proto__ === per.prototype.constructor.prototype) // true

将函数用作构造函数时,Constructor.prototype 属性将成为构造函数实例的 [[Prototype]]

同时 Constructor 是指向该函数的。