原型继承
思考如下代码:
function Foo (name) {
this.name = name
}
Foo.prototype.myName = function () {
return this.name
}
function Bar (name, label) {
Foo.call(this, name)
this.label = label
}
Bar.prototype = Object.create(Foo.prototype)
Bar.prototype.myLabel = function () {
return this.label
}
const a = new Bar('limei', 'special')
a.myName() // 'limei'
a.myLabel() // 'special'
稍微说一下,在Bar构造函数中,存在的this属于绑定规则的显式绑定,call方法会将Bar函数执行上下文用于Foo函数的执行,在new Bar()
执行时,Foo构造函数内的属性name也会被赋值。
在上述代码中,最重要的表达式是Bar.prototype = Object.create(Foo.prototype)
,意思是新创建一个对象,赋值给Bar.prototype
,并将新对象的[[prototype]]
属性指向Foo.prototype
。
我们对比下:
Bar.prototype = Foo.prototype
如果是直接赋值呢,不同的地方在哪?
其实有本质区别,通过create
方法创建会生成一个新对象,只是新对象的[[prototype]]
会与原对象进行关联;而直接赋值,不会创建新对象,只是把引用同时赋值给了Bar.prototype
,这样,当有任何修改,都会同步,而创建的新对象则不会。
还有个问题,在const a = new Bar('limei', 'special')
的时候,会给变量a一个默认的[[prototype]]
值Bar.prototype
,这里就相当于手动赋值了,而放弃了原来的默认指向值。虽然上面那种方式完全不符合我们的要求,但通过new Foo()
呢?就像刚才所说,执行new Foo()
的时候会将新对象的[[prototype]]
指向Foo.prototype
,这样不就符合了嘛。
Bar.prototype = new Foo()
我们其实想要的是能最小副作用的修改原型属性的指向,通过这种方式可以,只是这样会调用Foo构造函数,如果在构造函数有些副作用操作,则不可控。
修改[[prototype]]
我们从对象api的方式来看,都存在一个属性__proto__
,这个在有的浏览器是可以修改[[prototype]]值的,只是这个方法不标准,且兼容性不是很好,不常用。
在ES6中添加了辅助函数Object.setPrototypeOf()
来实现:
Object.setPrototypeOf(Bar.prototype, Foo.prototype)
这两种方式,各有优劣:create方式性能稍微差一点,setPrototypeOf方法可读性不如create。
原型继承了什么
一句话总结:它继承了对象间的关联关系。