深刻学习 es6 class

es6 引入的 class 类实质上是 JavaScript 基于原型继承的语法糖。javascript

function Animal(name) {
  this.name = name;
}

Animal.prototype.sayHi = () => {
  return `Hello ${this.name}`;
};

// 等同于

class Animal {
  constructor(name) {
    this.name = name;
  }

  sayHi() {
    return `Hello ${this.name}`;
  }
}
复制代码

类由两部分组成:类声明,类表达式java

  1. 类声明
class Animal {
  constructor(name) {
    this.name = name;
  }
}

复制代码

类其实是个特殊的函数,普通函数声明和类函数声明有一个重要的区别就是函数 声明会提高,而类声明不会。若是先访问,后声明就会抛出相似于下面的错误。es6

let animal = new Animal();
// Uncaught ReferenceError: Cannot access 'Animal' before initialization

class Animal {}
复制代码
  1. 类表达式

类表达式能够是被命名的或匿名的,(ps: 类表达式也一样受到类声明中提到的提高问题的困扰。)函数

// 匿名类
let Animal = class {
  constructor(name) {
    this.name = name;
  }
};

// 命名类
let Animal = class Cat {
  constructor(name) {
    this.name = name;
  }

  getClassName() {
    return Cat.name;
  }
};
复制代码

此时类名字Cat只能在 class 内部使用,指代当前类,在类的外部只能用Animalui

  1. 构造函数 constructor方法是类的默认方法,经过new建立对象实例时,自动会调用该方法, 一个类必须拥有constructor方法,若是没有写,JavaScript 引擎会默认加上空的constructor方法。
class Animal {}

// 等同于

class Animal {
  constructor() {}
}
复制代码

constructor方法默认返回实例对象(既this),彻底能够指定返回另一个对象this

class Animal {
  constructor() {
    return Object.create(null);
  }
}

new Animal() instanceof Animal; // false;
复制代码

上面代码中,constructor函数返回一个全新的对象,结果致使实例对象不是Animal类的实例。spa

  1. 严格模式 类和模块的内部,默认就是严格模式,好比,构造函数,静态方法,原型方法,getter 和 setter 都在严格模式下执行。prototype

  2. 类的实例 类的实例,经过 new 建立, 建立时会自动调用构造函数code

class Animal {
  constructor(name) {
    this.name = name;
  }

  sayHi() {
    return "My name is " + this.name;
  }
}

let animal = new Animal("rudy");
animal.sayHi(); // My name is rudy
复制代码
  1. 存取器 与 ES5 同样,在类的内部可使用getset关键字,对某个属性设置存取 函数和取值函数,拦截该属性的存取行为。
class Animal {
  constructor(name) {
    this.name = name;
  }

  get name() {
    return "rudy";
  }

  set name(value) {
    console.log("setter, " + this.value);
  }
}

let animal = new Animal("rudy");
animal.name = "Tom"; // setter, Tom
console.log(a.name); // rudy
复制代码
  1. 静态方法 使用static修饰符修饰的方法称为静态,它们不须要实例化,直接经过类来调用。
class Animal {
  static sayHi(name) {
    console.log("i am " + name);
  }
}

let animal = new Animal();
Animal.sayHi("rudy"); // i am rudy
animal.sayHi("rudy"); // Uncaught TypeError: animal.sayHi is not a function
复制代码
  1. 实例属性,静态属性 ES6 中的实例属性只能经过构造函数中的this.xxx来定义,但最近 ES7 中能够直接在类里面定义:
class Animal {
  name = "rudy";
  static value = 11;
  sayHi() {
    console.log(`hello, ${this.name}`);
  }
}

let animal = new Animal();
animal.sayHi(); // hello, rudy
Animal.value; // 11
animal.value; // undefiend
复制代码
  1. 类的继承 使用extends关键字实现继承,子类中使用super关键字来调用父类的构造函数和方法。
class Animal {
  constructor(name) {
    this.name = name;
  }

  sayHi() {
    return "this is " + this.name;
  }
}

class Cat extends Animal {
  constructor(name, value) {
    super(name); // 调用父类的 constructor(name)
    this.value = value;
  }

  sayHi() {
    return `omg, ${super.sayHi()} it is ${this.value}`;
  }
}

let cat = new Cat("Tom", 11);
cat.sayHi(); // omg, this is Tom it is 11;
复制代码
  1. super 关键字 super这个关键字,既能够当着函数使用,也能够当着对象使用。两种状况下,用法彻底不一样。

第一种状况,super做为函数调用时,表明父类的构造函数。ES6 要求,字类的构造函数必须执行一次super函数。对象

class Animal {}

class Cat extends Animal {
  constructor() {
    super();
  }
}
复制代码

上面代码中,子类Cat的构造函数中的super(),表明调用父类的构造函数,这是必须的,否在 JavaScript 引擎会报错。 注意,super虽然表明了父类Animal的构造函数,可是返回的是字类Cat的实例,既super内部的this指的是Cat的实例,所以super()在这里至关于 Animal.prototype.constructor.call(this)

class Animal {
  constructor() {
    console.log(new.target.name); // new.target指向当前正在执行的函数
  }
}

class Cat extends Animal {
  constructor() {
    super();
  }
}

new Animal(); // Animal;
new Cat(); // Cat;
复制代码

能够看出,在super()执行时,它指向的是子类Cat的构造函数,而不是父类Animal的构造函数,也就是说super内部的this指向是Cat

做为函数时,super()只能用在子类的构造函数之中,用在其余地方就会报错。

class Animal {}

class Cat extends Animal {
  hi() {
    super(); // Uncaught SyntaxError: 'super' keyword unexpected here
  }
}
复制代码

第二种状况,super做为对象时:

  1. 在普通方法中,指向父类的原型对象,
  2. 在静态方法中,指向父类。
class Animal {
  getName() {
    return "rudy";
  }
}

class Cat extends Animal {
  constructor() {
    super();
    console.log(super.getName());
  }
}

let cat = new Cat(); // rudy;
复制代码

上面代码中,子类Cat中的super.getName(),就是将super看成一个对象使用,这时,super在普通方法中,指向的是Animal.prototypesuper.getName()至关于Animal.prototype.getName()

这里须要注意,因为super指向的是父类原型对象,因此定义在父类实例上的方法和属性,是没法经过super获取到的。

class Animal {
  constructor() {
    this.name = "rudy";
  }
}

class Cat extends Animal {
  constructor() {
    super();
  }
  getName() {
    return super.name;
  }
}

let cat = new Cat();
cat.getName(); // undefined;
复制代码

上面代码中,name是父类实例的属性,而不是父类原型对象的属性,因此super.name引用不到它。

用在静态方法中,super将指向父类,而不是父类的原型对象。

class Animal {
  static getName(name) {
    console.log("static", name);
  }

  getName(name) {
    console.log("instance", name);
  }
}

class Cat extends Animal {
  constructor() {
    super();
  }

  static getName(name) {
    super.getName(name);
  }

  getName(name) {
    super.getName(name);
  }
}

Cat.getName("rudy"); // static rudy;

let cat = new Cat();
cat.getName("tom"); // instance tom;
复制代码

在上面代码中,super在静态方法中指向父类,在普通方法中指向父类的原型对象。

另外,在字类的静态方法中经过super调用父类的方法时,方法内部的this指向当前的子类,而不是子类实例。

class Animal {
  constructor() {
    this.name = "rudy";
  }

  static print() {
    console.log(this.name);
  }
}

class Cat extends Animal {
  constructor() {
    super();
    this.name = 2;
  }

  static print() {
    super.print();
  }
}

Cat.name = "Tom";
Cat.print(); // Tom;
复制代码

参考资料

  1. ES6
  2. TS class类
相关文章
相关标签/搜索