美文网首页
ES5和ES6的继承

ES5和ES6的继承

作者: my木子 | 来源:发表于2021-05-30 10:21 被阅读0次

    继承

    • 继承,即一个对象上拥有被继承对象的属性和方法
    • OOP 面向对象编程 中,通过类的继承来实现代码的复用,通过实例化一个类可以创建许多对象,在 JS 中继承是通过原型实现的。
    ES5 / ES6 继承比较

    ES5继承

    构造函数、原型和实例的关系
    • 每一个构造函数都有一个原型对象
    • 每一个原型对象都有一个指向构造函数的指针
    • 每一个实例都包含一个指向原型对象的内部指针
    1. 原型链继承
    • 思路
      将父类的实例作为子类的原型
    • 优点
      1、父类方法可以复用
    • 缺点
      1、父类的所有 引用属性(如:loves) 会被所有子类共享,当其中一个子类的引用属性被修改后,会影响其他子类
      2、子类型实例不能给父类型构造函数传参
        function Parent() {
          this.name = '无法传参';
          this.loves = ['敲代码', '摄影']
        }
    
        Parent.prototype.getName = function () {
          console.log(this.name);
        }
        Parent.prototype.getLoves = function () {
          console.log(this.loves);
        }
    
        function Child(age) {
          this.age = age
        }
        // 子类继承父类的属性
        Child.prototype = new Parent()
        Child.prototype.getAge = function () {
          console.log(this.age);
        }
    
        let zs = new Child(23);
        zs.getAge();
        zs.getName();
        zs.loves.push('打游戏');
        zs.getLoves();
    
        let ls = new Child(22)
        ls.getAge();
        ls.getName();
        ls.getLoves();
    
    2. 借用构造函数继承
    • 基本思想
      在子类构造函数中调用父类构造函数,可以在子类构造函数中使用 call()apply() 方法
    • 优势
      1、可以在子类构造函数中向父类传参数
      2、父类的引用属性不会被共享
    • 缺点
      1、子类不能访问父类原型 User.prototype 上定义的方法,因此所有方法属性都写在构造函数中,每次创建实例都会初始化
        function Parent(name) {
          this.name = name;
          this.loves = ['敲代码', '摄影']
        }
    
        Parent.prototype.getName = function () {
          console.log(this.name);
        }
        Parent.prototype.getLoves = function () {
          console.log(this.loves);
        }
    
        function Child(name, age) {
          // 子类继承父类的属性
          Parent.call(this, name);
          // 实例属性
          this.age = age
        }
        Child.prototype.getAge = function () {
          console.log(this.age);
        }
    
        let zs = new Child('张三', 23);
        console.log(zs);
        zs.getAge();
        // zs.getName();  //  报错,子类不能访问父类原型上定义的方法
        zs.loves.push('打游戏');
        // zs.getLoves(); //  报错,子类不能访问父类原型上定义的方法
        console.log(zs.name);
        console.log(zs.loves);
    
        let ls = new Child('李四', 22)
        ls.getAge();
        console.log(zs.name);
        console.log(zs.loves);
    
    3. 组合继承
    • 将原型链和借用构造函数技术组合到一起实现继承

    • 优点
      1、父类的方法可以复用
      2、可以在子类构造函数中向父类传参数
      3、父类构造函数中的引用属性不会被共享

    • 缺点
      1、会调用两次超类型构造函数,一次是在创建子类型原型的时候,一次是在子类型构造函数的内部

           function Parent(name) {
          this.name = name;
          this.loves = ['敲代码', '摄影']
        }
    
        Parent.prototype.getName = function () {
          console.log(this.name);
        }
        Parent.prototype.getLoves = function () {
          console.log(this.loves);
        }
    
        function Child(name, age) {
          // 子类继承父类的属性
          Parent.call(this, name);
          // 实例属性
          this.age = age
        }
         // 子类继承父类的属性
        Child.prototype = new Parent();
        Child.prototype.getAge = function () {
          console.log(this.age);
        }
    
        let zs = new Child('张三', 23);
        console.log(zs);
        zs.getAge();
        zs.getName();
        zs.loves.push('打游戏');
        zs.getLoves();
    
        let ls = new Child('李四', 22)
        ls.getAge();
        ls.getLoves();
    
    4. 原型式继承
    • 浅拷贝参数对象
    • 优点
      父类方法可复用
    • 缺点
      1、父类的所有 引用属性(如:loves) 会被所有子类共享,当其中一个子类的引用属性被修改后,会影响其他子类
      2、子类实例不能向父类传参
        function objectCopy(obj) {
          function Fun() { };
          Fun.prototype = obj;
          return new Fun();
        }
    
        let person = {
          name: null,
          age: null,
          loves: ['敲代码', '摄影'],
          getName: function () {
            console.log(this.name);
          },
          getAge: function () {
            console.log(this.age);
          },
          getLoves: function () {
            console.log(this.loves);
          }
        }
    
        let zs = objectCopy(person);
        // let zs = Object.create(person);
        // let zs = Object.assign(person);
        console.log(zs);
        zs.name = '张三';
        zs.age = 18;
        zs.loves.push('打游戏');
        zs.getAge();
        zs.getName();
        zs.getLoves();
    
        let ls = objectCopy(person);
        // let zs = Object.create(person);
        // let zs = Object.assign(person);
        ls.name = '李四';
        ls.age = 20;
        ls.getAge();
        ls.getName();
        ls.getLoves();
    
    • ES5的 Object.create() 方法在只有第一个参数时,与这里的 objectCopy() 方法效果相同
    • ES6的 Object.assign 与这里的 objectCopy() 方法效果相同
    5. 寄生式继承
    • 使用原型式继承对目标对象进行浅复制,增强这个浅复制的能力
        let info = {
          loginType: 'PC',
          name: null,
          loves: ['敲代码']
        }
    
        function objectCopy(obj) {
          function Fun() { };
          Fun.prototype = obj;
          return new Fun()
        }
        function createUser(original) {
          // 通过调用函数创建一个新对象
          let clone = objectCopy(original);
          // 增强对象
          clone.getInfo = function () {
            console.log(this.name);
          };
          clone.getLove = function () {
            console.log(this.loves);
          };
          return clone;
        }
    
        let zs = createUser(info)
        zs.name = '张三';
        zs.loves.push('摄影');
        zs.getInfo()
    
        let ls = createUser(info)
        ls.name = '张三';
        ls.getInfo()
        ls.getLove()
    
    6. 寄生式组合继承
    • 寄生式组合继承可以算是引用类型继承的最佳模式
    • 优点
      子类可以向父类传参
      父类方法可以复用
      父类的引用属性不会被共享
      只调用一次父类构造函数
        function objectCopy(obj) {
          function Fun() { };
          Fun.prototype = obj;
          return new Fun();
        }
    
        function inheritPrototype(child, parent) {
          let prototype = objectCopy(parent.prototype); // 创建对象
          prototype.constructor = child; // 增强对象
          child.prototype = prototype; // 赋值对象
        }
    
        function Parent(name) {
          this.name = name;
          this.loves = ['敲代码', '摄影']
        }
    
        Parent.prototype.getName = function () {
          console.log(this.name);
        }
        Parent.prototype.getLoves = function () {
          console.log(this.loves);
        }
    
        function Child(name, age) {
          // 子类继承父类属性
          Parent.call(this, name);
          this.age = age;
        }
    
        // 子类继承父类方法
        inheritPrototype(Child, Parent);
        Child.prototype.getAge = function () {
          console.log(this.age);
        }
    
        let zs = new Child('张三', 23);
        zs.getAge();
        zs.getName();
        zs.loves.push('打游戏');
        zs.getLoves();
    
        let ls = new Child('李四', 22)
        ls.getAge();
        ls.getName();
        ls.getLoves();
    

    ES6 继承

    • ES6 通过 class 之间通过使用 extends 关键字来实现继承
         class Parent {
          constructor(name) {
            this.name = name;
            this.loves = ['敲代码', '摄影']
          }
          getName() {
            console.log(this.name)
          };
          getLoves() {
            console.log(this.loves)
          };
        }
    
        class Child extends Parent {
          constructor(name, age) {
            super(name);  // 调用父类的构造函数
            this.age = age;
          };
          getAge() {
            console.log(this.age)
          };
        }
    
        let zs = new Child('张三', 23);
        zs.getAge();
        zs.getName();
        zs.loves.push('打游戏');
        zs.getLoves();
    
        let ls = new Child('李四', 22)
        ls.getAge();
        ls.getName();
        ls.getLoves();
    
    console.log(zs)

    总结

    • 声明的区别
      函数声明会提升,类声明不会
    • 继承的区别
      ES5的继承实质上是先创建子类的实例对象,然后再将父类的方法添加到this上
      ES6的继承实质上是先创建父类的实例对象this,然后再用子类的构造函数修改this。因为子类没有自己的this对象,所以必须先调用父类的super()方法,否则新建实例报错。

    相关文章

      网友评论

          本文标题:ES5和ES6的继承

          本文链接:https://www.haomeiwen.com/subject/cothsltx.html