From 犀牛书
继承
JavaScript中的所有数据类型都可以归结为对象,因此为了将所有对象联系起来,就需要继承机制。
首先JavaScript通过 new
,从原型对象生成实例对象。但是,JavaScript(一下简称JS)又没有引入“类”,因此JS的 new
调用的不是类,而是“构造函数”。
比如像这样,构建一个猫对象的原型:
对此构造函数使用new
生成实例:
然而,通过this
创建的属性会被实例对象独立继承拷贝,相对独立,并非共有,也会浪费内存。因此JS还提供了prototype模式,用来创建共有属性。每个构造函数都有一个prototype
属性,指向一个对象该对象的所有属性和方法,都会被构造函数的实例继承。
这样一来,实例对象一旦创建,会自动引用prototype
对象的属性和方法。共享属性和方法放在prototype
对象中,是引用的;不共享的属性和方法放在构造函数内部,是本地的:
生成实例则是这样:
如果再实例话一个对象,起共有属性和方法均指向prototype
对象,因此提高了运行效率:
还有一些验证继承模式的方法:
1.实例会自动含有一个constructor
属性,指向它的构造函数:
2.通过instanceof
运算符可以验证原型对象和实例对象的关系:
3.通过isPrototypeOf()
判断原型对象和实例对象的关系:
4.通过hasOwnProperty()
判断某一属性是来自本地还是继承引用:
5.通过in
运算符判断某个实例是否含有某个属性(无论本地还是引用):
有关构造函数的继承
构造函数的继承,其实就是生成一个继承了多个对象的实例
像下面这样,有两个构造函数,需要实现Cat
继承Animal
:
有以下方法:
1.构造函数绑定。使用call
和apply
把父对象构造函数绑定在自对象上:
2.prototype模式。把子对象的prototype
属性指向父对象的实例:
这样prototype
对象原来的值被完全删除,包括指向其构造函数的constructor
属性,并赋予了一个新的值。
因此需要重新规定其构造函数的指向:
这样之后:
3.直接继承父对象的prototype
。可以让Cat跳过 Animal,直接继承Animal.prototype。
先设置父对象的prototype
:
再把Cat的prototype
指向Animal的prototype
,当然因为修改了prototype
,构造函数也要改:
这样做的结果是不错,但是因为把父对象和子对象的原型关联了,任何对Cat.prototype
的修改都会反映到Animal.prototype
,其实Animal.prototype
对象的constructor
属性也已经被修改成Cat
了。
因此最好的方法是利用空对象作为中介:
现将空对象的prototype
指向父对象的prototype
,再把子对象的prototype
属性指向空对象的实例,这其实是上面两种方法的结合。
4.prototype模式的封装函数,这是更有可用性的方法:
调用的时候像这样:
5.纯粹拷贝方法,把父对象的所有属性和方法拷贝进子对象:
有关非构造函数的继承
所谓非构造函数,就是普通对象,像下面这样,有两个普通对象,需要实现Cat
继承Animal
:
有以下方法:
1.使用object()函数。object()函数就是把子对象的prototype
属性指向父对象,使子对象与父对象连在一起:
因此先在父对象的基础上生成子对象:
子对象随后继承了父对象的属性:
2.浅拷贝。把父对象的属性全部拷贝给子对象,实现继承:
调用的时候像这样:
浅拷贝存在一个问题:
如果父对象的属性等于数组或另一个对象,那么子对象获得的只是一个内存地址引用,而不是真正拷贝,因此存在父对象可能被篡改。
3.深拷贝。用来完成包括数组和对象的拷贝。
调用的时候依然这样:
先到这里~~