继承,代码复用的一种模式。和其它高级程序语言相比,javascript有点点不一样,它是一门纯面向对象的语言,在JS中,没有类的概念,但也可以通过原型(prototype)来模拟对象的继承和多态。
原型与实例
- 一切对象都是Object的实例,一切函数都是Function的实例
- 构造函数通过 prototype 属性访问原型对象
- 实例对象通过 [[prototype]] 内部属性访问原型对象,浏览器实现了 proto 属性用于实例对象访问原型对象
- Object 是构造函数,既然是函数,那么就是Function的实例对象;Function是构造函数,但Function.prototype是对象,既然是对象,那么就是Object的实例对象
关系判断
- instanceof 判断是否为另一个对象的实例
- isPrototypeOf() 判断一个对象是否存在于另一个对象的原型链上 Child.isPrototypeOf(Parent) //true
- Object.getPrototypeOf() ES6中新增的方法,用于获取子类的父类 Object.getPrototypeOf(Child) == Parent //true
继承类型
- 引用对象继承:子引用类型继承父引用类型,然后通过子引用类型生成的实例对象,具有父引用类型的特性。
- 实例对象继承:而实例对象继承,继承得到的对象都具有父实例对象的所有属性和方法,其实就是指对象的复制和克隆。
引用对象继承
- 原型 (C.prototype = new P())
- 构造函数 (P.apply(this, arguments))
- 原型 + 构造函数 (使用原型链实现对原型中的属性方法的继承,使用构造函数实现实例属性的继承)
- 共享原型(子类与父类共享同一个原型)
- 临时原型(使用中间类)
- 临时原型 + 构造函数(完美、nodejs的继承方式、ES5版本)
原型继承
让子类的的原型等于父类的实例,从而继承父类的所有属性和原型
function Parent(){ |
缺点:
- 不能向父类的构造函数中传递参数
- 父类中的引用类型属性会被实例共享
- 需要修正实例的constructor指向,否则会指向父类
构造函数继承
在子类的构造函数中使用 Parent.call(this, …args) 或 Parent.apply(this,[args]) 来继承父类的属性,并向父类的构造函数传参
function Parent(name){ |
- 优点:子类可以向父类的构造函数中传参,子类实例中的引用类型属性互不干扰
- 缺点:子类实例无法访问父类的原型(无法复用父类原型中的方法)
原型+构造函数继承
为解决纯原型继承不能给父传参和纯构造函数继承不能继承父类原型的缺点,把二者结合起来实现
function Parent(name){ |
缺点:子类初次实例化时会多调用一个父类的构造函数(第一次创建子类原型和子类实例化时)
共享原型
子类和父类共用一个原型
function inherit(C, P){ |
缺点:子类实例化时,父类构造函数接收不到参数, 子类原型如果改变也会影响到父类原型
临时原型
使用一个纯净类继承父类的原型,再将纯净类的实例设置为子类的原型,如此子类继承了父类的原型和纯净类的构造函数,再修正一下子类的构造函数为子类本身,这样子类的原型改动就不影响父类。
function inherit(C, P){ |
缺点:只继承了父类的原型,子类实例化时一样不能给父类的构造函数传参
临时原型 + 构造函数
在子类的构造函数中调用父类的构造函数,修复了上面 临时原型 不能传递参数给父类构造函数的问题
function inherit(C, P){ |
实例对象继承
- 原型实例
- 克隆(深拷贝、浅拷贝)
- 借用和绑定
原型实例
创建一个继承父类原型的实例对象,这也是ES5中Object.create()的简单实现
function object(P){ |
克隆
浅拷贝只能拷贝值类型的数据,对于引用类型,只会拷贝引用地址,如果有引用类型,多个拷贝对象会共用同一个引用类型的数据,造成混乱。
function clone(parent, child){ |
两个拷贝对象共用同一个引用类型,会相互影响
深拷贝
function cloneDeep(parent, child){ |
两个对象引用不同的引用地址,互不影响
借用和绑定
使用 call / apply / bind 复用对象上的方法
var parent = { |
ES6的继承方式
使用 extends 关键字,来实现继承
class Parent { |
- ES5继承 是先创建子类的实例对象this,再向this对象中添加父类的方法
- ES6继承 是先创造父类的实例对象this, 再用子类的构造函数修改this
- Parent 可以是任意函数(只要有prototype属性)