原型链继承
子函数的原型指向父函数的实例
不足:引用类型的属性会被公用,比如下面的colors,多个子类继承,会被共享
//? 引用类型的属性会被公用,这个缺点不容忽视function Father() {this.name = 'father'this.colors = [1,3]}Father.prototype.sayHi = function() {console.log('Father say Hi~');}function Child() {}Child.prototype = new Father()let child = new Child()let child2 = new Child()child.sayHi();child.colors.push(2)console.log(child.colors) // [1,3,2]console.log(child2.colors) // [1,3,2]
伪构造函数继承(经典继承)
通过call/apply来进行父函数的调用,使得可以复制一份父函数的内部属性
不足:方法必须写在构造函数内部,所以会引起相同函数的内存浪费,也就是没实例化一个函数,都会复制一份一模一样但是保存地址不一样的函数
function Father(name) {this.name = namethis.colors = [1,2]}Father.prototype.sayHi = function() {console.log('xxxx')}function Child(name) {Father.call(this, name)}let child1 = new Child('xjx')let child2 = new Child('ls')child1.colors.push(333)// child1.sayHi() // 会报错console.log(child1.name) // xjxconsole.log(child1.colors, child2.colors) // [1,2,333] [1,2]
组合继承
融合了原型链继承和经典继承,解决了引用类型和函数重复声明的问题
使用最多
不足:Father构造函数会被调用两次,导致实例和子函数的原型上都有属性,造成不需要的浪费和降低运行效率
function Father(name) {this.name = namethis.colors = [1,2]}Father.prototype.sayHi = function() {console.log('Father say hi~');}function Child(name) {Father.call(this, name)}Child.prototype = new Father('xjx');let child = new Child('ls')let child1 = new Child('xx')console.log(child.name) // lschild.sayHi() // Father say hi~child.colors.push(3)console.log(child.colors, child1.colors) // [1,2,3] [1,2]
原型式继承
相当于ES5的Object.create()方法
传入的是个对象,所以相当于给prototype换了一个原型对象,所以跟原型继承很像,也是有引用类型的问题
不足:引用类型问题
function initialFn(o) {function fn() {}fn.prototype = oreturn new fn()}let Obj = {name: 'xjx',colors: [1,3],sayHi: function() {console.log('father say hi~')}}let child = initialFn(Obj)let child2 = initialFn(Obj);console.log(child.name) // xjxchild.colors.push('222')child.sayHi() // father say hi~console.log(child2.colors, child.colors) // [1,3,'222'] [1,3,'222']
寄生式继承
不足:
函数无法重用
引用类型问题仍然存在
function initialFn(o) {let fn = Object(o);fn.sayHi = function() {console.log('father say hi~')}return fn;}let father = {name: 'xjx',colors: [1,3],}let child = initialFn(father)let child1 = initialFn(father)child.colors.push(111)console.log(child.colors, child1.colors) // [1,3,111] [1,3,111]child.sayHi()
寄生式组合继承
融合了组合式和寄生式的特点
解决组合式重复执行父级构造函数的问题,提高运行效率
解决了寄生式函数重用的问题
function Father(name) {this.name = namethis.colors = [1,2]}Father.prototype.sayHi = function() {console.log('father say hi~')}// 把内部属性拿到function Child(name) {Father.call(this, name);}// 把prototype上的属性拿到function initialFn(fatherFn, childFn) {// 保留父函数的副本let prototype = Object(fatherFn.prototype);// 还原子函数的构造函数指向prototype.constructor = childFn;childFn.prototype = prototype;}initialFn(Father, Child);let child = new Child('xjx')let child1 = new Child('ls')child.colors.push(1122)console.log(child.colors, child1.colors) // [1,2, 1122] [1,2]child.sayHi() // father say hi~
