aermin / blog

📝 My blog / notes

Home Page:https://www.aermin.top/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ES6的类&&ES6前的继承

aermin opened this issue · comments

在ES6出来前,js并无类,与类最接近的是:创建一个构造器,然后将方法指派到 该构造器的原型上。
可以通过我之前结合高程这本书整理的文章看看,js面对对象(创建对象,实现继承)

接下来,根据代码看看两者之间的联系,在原有的基础上吸收使用ES6的类。

ES6 之前,实现自定义类型的继承是个繁琐的过程。严格的继承要求有多个步骤。

ES6前的代码实现

function Rectangle(length, width) {   // ①
  this.length = length;
  this.width = width;
  this.test = "test~" 
}

Rectangle.prototype.getArea = function() {   // ②
    return this.length * this.width;
};

Rectangle.prototype.doTest = function() {   // ②
    console.log(this.test);
};
  
function Square(length) {  // ③
    Rectangle.call(this, length, length);  //构造器继承属性
}
  
Square.prototype = Object.create(Rectangle.prototype, {   //④ 原型继承方法,且书写配置 
    constructor: {
      value:Square,
      enumerable: true,   //可枚举
      writable: true,      //可写
      configurable: true  //可配置
    }
});
 
Square.prototype.getDataFromSuperClass = function(){
  console.log(this.length ,"===", this.width);
}

var square = new Square(3);  //创建实例
console.log(square.getArea());// 9
square.doTest(); //"test~"
square.getDataFromSuperClass();//3 "===" 3
console.log(square instanceof Square);// true
console.log(square instanceof Rectangle);// true

这例子和js面对对象(创建对象,实现继承) 的例子基本一样

ES6的代码实现(类)

class Rectangle {
  constructor(length, width) {  //①
    this.length = length;
    this.width = width;
    this.test = "test~"  //在constructor定义的this.变量 都将可以在class中访问 (有木有vue的data既视感😄)
  }

  getArea() {  //②
    return this.length * this.width;  
  }

  doTest () {
    console.log(this.test);
    console.log("theWidth in super class", this.theWidth); //使用本类中的取值函数 
  }

  get theWidth() { //取值函数
    return this.width;
  }
  
}

class Square extends Rectangle {  //extends 方法相当于 ④
  constructor(length) {       
    super(length, length);   // ③  super() 用来访问父类的构造器   与 Rectangle.call(this, length, length) 相同  
  }
  
  getDataFromSuperClass(){   //因为super  所以这边可以访问到父类Rectangle的this.变量
     console.log(this.length ,"===", this.width); 
     console.log("theWidth in sub class ", this.theWidth); //使用父类的取值函数 
  }

}
var square = new Square(3);
console.log(square.getArea());// 9
square.doTest(); //"test~"  "theWidth in super class" 3
square.getDataFromSuperClass();//3 "===" 3  "theWidth in sub class " 3
console.log(square instanceof Square);// true
console.log(square instanceof Rectangle);// true

两个例子我都用序号标了,序号相同的代表着不同的写法,一样的意思

继承了其他类的类(如上文的Square)被称为派生类( derived classes )。

如果派生类指定了构造器,就需要 使用 super() ,否则会造成错误。若你选择不使用构造器, super() 方法会被自动调用, 并会使用创建新实例时提供的所有参数。

class Square extends Rectangle {// 没有构造器
}

// 等价于:

class Square extends Rectangle {
    constructor(...args) {
        super(...args);
    }
}

此例中的第二个类展示了与所有派生类默认构造器等价的写法,所有的参数都按顺序传递给 了基类的构造器。在当前需求下,这种做法并不完全准确,因为 Square 构造器只需要单个 参数,因此最好手动定义构造器

使用super()需注意

  • 你只能在派生类中使用super()。若尝试在非派生的类(即:没有使用extends关键字的类)或函数中使用它,就会抛出错误。
  • 在构造器中,你必须在访问 this 之前调用 super() 。由于 super() 负责初始化this(ES5的.call()),因此试图先访问this自然就会造成错误。
  • 唯一能避免调用 super()的办法,是从类构造器中返回一个对象。

class 的静态方法

在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。要直接在Foo类上调用(Foo.classMethod()),而不是在Foo类的实例上调用(会报错),调用静态方法不需要实例化该类,但不能通过一个类实例调用静态方法。

class Foo {
  static bar () {
    this.baz();
  }
  static baz () {  //静态成员
    console.log('hello');
  }
  baz () { //实例对象的成员, 如 new Foo();
    console.log('world');
  }
}

Foo.bar() // hello
class Foo {
  bar () {
    this.baz();
  }
  static baz () {
    console.log('hello');
  }
  baz () {
    console.log('world');
  }
}
const newFoo = new Foo();

newFoo.bar()  // world

静态方法只能访问静态成员,实例方法只能访问实例成员。
然后为啥要有静态方法呢,有些东西是不需要实例的,只要有类就存在的,比如Array.isArray(obj); 静态方法通常用于为一个应用程序创建工具函数。

未完待续....