zoukankan      html  css  js  c++  java
  • class(二)--派生类的继承

    前言

    从我之前的一篇笔记对象的继承中, 我们可以知道JS的继承方式依赖原型链,而比较好的继承方式是寄生组合式继承
    先来温习下什么是寄生组合式继承

    function Rectangle(length, width) {
      this.length = length;
      this.width = width;
    }
    Rectangle.prototype.getArea = function() {
      return this.length * this.width;
    };
    function Square(length) {
      Rectangle.call(this, length, length);
      // 1 调用父类构造函数
    }
    Square.prototype = Object.create(Rectangle.prototype, {
      constructor: {
        value:Square,
        enumerable: true,
        writable: true,
        configurable: true
      }
    });
    // 2 Object.create会在内部创建一个空对象来连接两个原型对象, 再手动将constructor指向自身
    
    var square = new Square(3);
    console.log(square.getArea()); // 9
    console.log(square instanceof Square); // true
    console.log(square instanceof Rectangle); // true
    
    1. 在子构造函数中调用父级构造函数,来将属性定义在自己身上
    2. 原型继承主要是为了继承原型对象上的方法

    这就是在ES6之前的继承方式,对原型链理解不够深透的话很容易混乱
    但在ES6推出class这个类之后,一切都变得简单(再次提醒:class只是语法糖,实质就是对寄生组合式继承的封装

    派生类进行继承

    首先我们将继承其他类的的类称为派生类(derived classes), 再来看看上面的例子, 利用class如何轻松实现

    class Rectangle {
      constructor(length, width) {
        this.length = length;
        this.width = width;
      }
      getArea() {
        return this.length * this.width;
      }
    }
    class Square extends Rectangle {
      constructor(length) {
        // 与 Rectangle.call(this, length, length) 相同
        super(length, length);
      }
    }
    var square = new Square(3);
    console.log(square.getArea()); // 9
    console.log(square instanceof Square); // true
    console.log(square instanceof Rectangle); // true
    console.log(Square.prototype.constructor === Square) // true
    

    实现继承只需要两步

    1. classextends继承父类
    2. 在构造函数中调用super()

    super也是ES6中的内容,它是指向当前对象的原型的一个指针,实际上就是Object.getPrototypeOf(this)的值。

    根据上面的例子我们来看下

    Object.getPrototypeOf(Square) === Rectangle // true
    

    super(length, lenght)应该是Rectangle(lenght, length)才对啊,其实在ES6中真正出现了方法的概念,从前只能说是一个函数属性, 在方法中会有一个内部属性[[HomeObject]]任何对super的引用都会使用[[HomeObject]]属性来判断要做什么。第一步是在[[HomeObject]]上调用Object.getPrototypeOf()来获取对原型的引用;最后,创建this绑定并调用该方法的对象。因此是Rectangle.call(this, length, length)

    注意点:

    1. 使用了extends就必须在构造器中调用super()
    2. super必须要在this前面
    3. 要想不用super()
    • 不用构造器, 它会默认创建,并按照顺序传入所有参数
    • return返回一个对象,不能是number等基本数据类型,否则return的是this
    class Rectangle {
      constructor(length, width) {
        this.length = length;
        this.width = width;
      }
      getArea() {
        return this.length * this.width;
      }
    }
    // 不传super()
    class Square extends Rectangle {
      constructor(length) {
      }
    }
    let s1 = new Square(3) //Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructorat new Square
    // 报错 必须在访问this前面调用super构造器或者在返回一个新的Square实例对象必须调用super构造器
    
    // 直接不传入构造器
    class Square extends Rectangle {}
    let s2 = new Square(3, 4)
    console.log(s2.getArea()) //12
    console.log(s2.width) // 4
    console.log(s2.length) // 3
    // 其实默认调用了构造器
    //class Square extends Rectangle {
    //  constructor(...args) {
    //    super(...args)
    //  }
    //}
    
    //  返回一个对象
    class Square extends Rectangle {
      constructor(length) {
        return {}
      }
    }
    let s2 = new Square()
    console.log(s2 instanceof Square) // false 
    

    继承静态成员

    其实从之前的super就能看出个大概了

    Object.getPrototypeOf(Square) === Rectangle // true
    

    因此Rectangle的静态成员自然可以被Square访问到

    Rectangle.create = function(l, w) {
    	return new Rectangle(l, w)
    }
    console.log('create' in Square) // true
    console.log(Square.hasOwnProperty('create')) // false
    

    继承非类

    其实只要一个表达式能够返回一个具有[[Construct]]属性的以及原型的函数,就可以对其实使用extends
    一般来说,对象的原型会在通过构造器Object.create() 方法创建该对象时被指定。而[[Construct]]只有箭头函数没有

    因此一下几种情况都是可以的

    function Parent() {}
    
    class Child extends Parent {
       
    }
    console.log(Child.prototype.__proto__ === Parent.prototype) // true
    console.log(Child.__proto__ === Parent) // true
    
    let SerializableMixin = {
      serialize() {
        return JSON.stringify(this);
      }
    };
    let AreaMixin = {
      getArea() {
        return this.length * this.width;
      }
    };
    function mixin(...mixins) {
      var base = function() {};
      Object.assign(base.prototype, ...mixins);
      return base;
    }
    class Square extends mixin(AreaMixin, SerializableMixin) {
      constructor(length) {
        super();
        this.length = length;
        this.width = length;
      }
    }
    var x = new Square(3);
    console.log(x.getArea()); // 9
    console.log(x.serialize()); // "{"length":3,"width":3}"
    
  • 相关阅读:
    linux内核中GNU C和标准C的区别
    linux内核中GNU C和标准C的区别
    Getting start with dbus in systemd (02)
    Getting start with dbus in systemd (01)
    Getting start with dbus in systemd (03)
    物理内存相关的三个数据结构
    数据类型对应字节数(32位,64位 int 占字节数)
    Linux kernel 内存
    共模电感的原理以及使用情况
    [原创]DC-DC输出端加电压会烧毁
  • 原文地址:https://www.cnblogs.com/guanine/p/9348565.html
Copyright © 2011-2022 走看看