继承和原型链
含义
当我们在使用 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 是指向该函数的。