《JavaScript 设计模式》总结
JavaScript 类的继承
大体上,每个类由三部分组成,第一部分是构造函数内的,这是供实例化对象复制用的;第二部分是构造函数外的,直接通过点语法添加的,这是供类使用的,实例化对象是访问不到的;第三部分是类的原型中的,实例化对象可以通过其原型链间接地访问到,也是为供所有实例化对象所使用的。
类式继承
1 | // 类式继承 |
新创建的对象复制了父类的构造函数内的属性与方法并且将原型 __proto__
指向了父类的原型对象,这样就拥有了父类的原型对象上的属性和方法,并且这个新创建的对象可直接访问到父类原型对象上的属性与方法。
弊端
- 父类的共有属性要是引用类型,就会在子类中被所有实例共有,因此一个子类的实例更改子类型从父类构造函数中继承来的共有属性就会直接影响到其他子类
- 子类的实现的继承是靠其原型
prototype
对父类的实例化实现的,因此在创建父类的时候,是无法向父类传递参数的,因此在实例化父类的时候也无法对父类构造函数内的属性进行初始化
构造函数式继承(创建即继承)
1 | // 声明父类 |
分析SuperClass.call(this, id)
这是构造函数式继承的精华,由于 call 这个方法可以改变函数的作用域,因此在子类中,对 SuperClass
调用这个方法就是将子类的变量在父类中执行一遍,由于父类是给 this
绑定属性的,那么子类自然也就继承了父类的共有属性。
弊端
这种继承方式没有涉及原型 prototype
, 所以父类的原型方法自然不会被子类继承,而如果想要被子类继承就必须放在构造函数中,这样的创建方式的每个实例都会单独的拥有一份而不能共用,也就违背了代码复用的原则。
组合继承
接上文,类式继承是通过子类原型的 prototype
对父类实例化来实现的,构造继承是通过在子类的构造函数作用环境中执行了一次父类的构造函数来实现的,组合继承整合这两点。
1 | function SuperClass(name) { |
测试代码
弊端
在使用构造函数继承是执行了一遍父类的构造函数,而在实现子类原型的类式继承又调用了一遍父类的构造函数,因此父类的构造函数调用了两遍。造成不必要的资源浪费。
原型式继承
借助原型 prototype 可以根据已有的对象创建一个新的对象,同时不必创建新的自定义对象类型。 – 道格拉斯·克罗克福德
1 | // 原型式继承 |
寄生式继承
1 | //声明基对象 |
寄生式继承其实就是对原型继承的二次封装,并且在第二次封装过程中对继承的对象进行了扩展,这样新创建的对象不仅仅有父类中的属性和方法而且添加新的属性和方法。
寄生组合式继承(目前最好的继承实现)
1 | /** |
多继承
1 | /** |
多态
ES6 class 实现继承
class
声明创建一个基于原型继承的具有给定名称的新类。
你也可以使用类表达式定义类。但是不同于类表达式,类声明不允许再次声明已经存在的类,否则将会抛出一个类型错误。
1 | class SuperClass { |