原型链继承

子函数的原型指向父函数的实例
不足:引用类型的属性会被公用,比如下面的colors,多个子类继承,会被共享

  1. //? 引用类型的属性会被公用,这个缺点不容忽视
  2. function Father() {
  3. this.name = 'father'
  4. this.colors = [1,3]
  5. }
  6. Father.prototype.sayHi = function() {
  7. console.log('Father say Hi~');
  8. }
  9. function Child() {}
  10. Child.prototype = new Father()
  11. let child = new Child()
  12. let child2 = new Child()
  13. child.sayHi();
  14. child.colors.push(2)
  15. console.log(child.colors) // [1,3,2]
  16. console.log(child2.colors) // [1,3,2]

伪构造函数继承(经典继承)

通过call/apply来进行父函数的调用,使得可以复制一份父函数的内部属性
不足:方法必须写在构造函数内部,所以会引起相同函数的内存浪费,也就是没实例化一个函数,都会复制一份一模一样但是保存地址不一样的函数

  1. function Father(name) {
  2. this.name = name
  3. this.colors = [1,2]
  4. }
  5. Father.prototype.sayHi = function() {
  6. console.log('xxxx')
  7. }
  8. function Child(name) {
  9. Father.call(this, name)
  10. }
  11. let child1 = new Child('xjx')
  12. let child2 = new Child('ls')
  13. child1.colors.push(333)
  14. // child1.sayHi() // 会报错
  15. console.log(child1.name) // xjx
  16. console.log(child1.colors, child2.colors) // [1,2,333] [1,2]

组合继承

融合了原型链继承和经典继承,解决了引用类型和函数重复声明的问题
使用最多
不足:Father构造函数会被调用两次,导致实例和子函数的原型上都有属性,造成不需要的浪费和降低运行效率

  1. function Father(name) {
  2. this.name = name
  3. this.colors = [1,2]
  4. }
  5. Father.prototype.sayHi = function() {
  6. console.log('Father say hi~');
  7. }
  8. function Child(name) {
  9. Father.call(this, name)
  10. }
  11. Child.prototype = new Father('xjx');
  12. let child = new Child('ls')
  13. let child1 = new Child('xx')
  14. console.log(child.name) // ls
  15. child.sayHi() // Father say hi~
  16. child.colors.push(3)
  17. console.log(child.colors, child1.colors) // [1,2,3] [1,2]

原型式继承

相当于ES5的Object.create()方法
传入的是个对象,所以相当于给prototype换了一个原型对象,所以跟原型继承很像,也是有引用类型的问题
不足:引用类型问题

  1. function initialFn(o) {
  2. function fn() {}
  3. fn.prototype = o
  4. return new fn()
  5. }
  6. let Obj = {
  7. name: 'xjx',
  8. colors: [1,3],
  9. sayHi: function() {
  10. console.log('father say hi~')
  11. }
  12. }
  13. let child = initialFn(Obj)
  14. let child2 = initialFn(Obj);
  15. console.log(child.name) // xjx
  16. child.colors.push('222')
  17. child.sayHi() // father say hi~
  18. console.log(child2.colors, child.colors) // [1,3,'222'] [1,3,'222']

寄生式继承

不足:
函数无法重用
引用类型问题仍然存在

  1. function initialFn(o) {
  2. let fn = Object(o);
  3. fn.sayHi = function() {
  4. console.log('father say hi~')
  5. }
  6. return fn;
  7. }
  8. let father = {
  9. name: 'xjx',
  10. colors: [1,3],
  11. }
  12. let child = initialFn(father)
  13. let child1 = initialFn(father)
  14. child.colors.push(111)
  15. console.log(child.colors, child1.colors) // [1,3,111] [1,3,111]
  16. child.sayHi()

寄生式组合继承

融合了组合式和寄生式的特点
解决组合式重复执行父级构造函数的问题,提高运行效率
解决了寄生式函数重用的问题

  1. function Father(name) {
  2. this.name = name
  3. this.colors = [1,2]
  4. }
  5. Father.prototype.sayHi = function() {
  6. console.log('father say hi~')
  7. }
  8. // 把内部属性拿到
  9. function Child(name) {
  10. Father.call(this, name);
  11. }
  12. // 把prototype上的属性拿到
  13. function initialFn(fatherFn, childFn) {
  14. // 保留父函数的副本
  15. let prototype = Object(fatherFn.prototype);
  16. // 还原子函数的构造函数指向
  17. prototype.constructor = childFn;
  18. childFn.prototype = prototype;
  19. }
  20. initialFn(Father, Child);
  21. let child = new Child('xjx')
  22. let child1 = new Child('ls')
  23. child.colors.push(1122)
  24. console.log(child.colors, child1.colors) // [1,2, 1122] [1,2]
  25. child.sayHi() // father say hi~