class类总结


1. class简单使用

class是一个语法糖,其底层还是通过 构造函数 去创建的。所以它的绝大部分功能,ES5 都可以做到。新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.sayName = function() {
    return this.name;
}

const xiaoming = new Person('小明', 18);
console.log(xiaoming);

上面代码用ES6的class实现,就是下面这样

class Person {
    constructor(name, age) {
      this.name = name;
      this.age = age;
    }
  
    sayName() {
      return this.name;
    }
}
const xiaoming = new Person('小明', 18)
console.log(xiaoming);
// { name: '小明', age: 18 }

console.log((typeof Person));
// function
console.log(Person === Person.prototype.constructor);
// true

2. constructor

每个类都必须要有一个 constructor,如果没有显示声明,js 引擎会自动给它添加一个空的构造函数:

class Person {

}
// 等同于
class Person {
  constructor () {

  }
}

3. 属性和方法

定义于 constructor 内的属性和方法,即定义在 this 上,属于实例属性和方法,否则属于原型属性和方法。

class Person {
  constructor (name) {
    this.name = name
  }

  say () {
    console.log('hello')
  }
}

let jon = new Person()

jon.hasOwnProperty('name') // true
jon.hasOwnProperty('say') // false

4. 属性表达式

let methodName = 'say'
class Person {
  constructor (name) {
    this.name = name
  }

  [methodName] () {
    console.log('hello')
  }
}

5. 静态方法

不需要通过实例对象,可以直接通过类来调用的方法,其中的 this 指向类本身

class Person {
  static doSay () {
    this.say()
  }
  static say () {
    console.log('hello')
  }
}
Person.doSay() // hello

静态方法可以被子类继承

// ...
class Sub extends Person {

}
Sub.doSay() // hello

可以通过 super 对象访问

// ...
class Sub extends Person {
  static nice () {
    return super.doSay()
  }
}
Sub.nice() // hello

6. 严格模式

不需要使用 use strict,因为只要代码写在类和模块内,就只能使用严格模式。

7. 提升

class 不存在变量提升。

new Person() // Uncaught ReferenceError: Person is not defined
class Person {

}

8. name 属性

name 属性返回了类的名字,即紧跟在 class 后面的名字。

class Person {

}
Person.name // Person

9. this

默认指向类的实例。

10. 取值函数(getter)和存值函数(setter)

class Person {
  get name () {
    return 'getter'
  }
  set name(val) {
    console.log('setter' + val)
  }
}

let jon = new Person()
jon.name = 'jon' // setter jon
jon.name // getter

11. class 表达式

如果需要,可为类定义一个类内部名字,如果不需要,可以省略:

// 需要在类内部使用类名
const Person = class Obj {
  getClassName () {
    return Obj.name //Obj
  }
}

// 不需要
const Person = class {}

立即执行的 Class:

let jon = new class {
  constructor(name) {
    this.name = name
  }
  sayName() {
    console.log(this.name)
  }
}('jon')

jon.sayName() //jon

12. 继承

12.1 简介

Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。

继承的基本使用

class Point {
  static hello() {
    console.log('hello world');
  }
  constructor(x,y){
    this.x = x;
    this.y = y;
  };
  toString(){
    return this.x +" "+this.y;
  };
};

class ColorPoint extends Point {
  constructor(x, y, color) {
    super(x, y); // 调用父类的constructor(x, y)
    this.color = color;
  };

  toString() {
    return this.color + ' ' + super.toString(); // 调用父类的toString()
  };
};

super关键字可以调用父类。constructor方法和toString方法之中,都出现了。

12.2 注意

(1) 一般情况写了constructor就要写super,否则new实例的时候会报错。

ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。这与ES6 class的继承机制完全不同;

class的继承,实质是先将父类实例对象的属性和方法加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。

class Point {
  constructor(x,y){
    this.x = x;
    this.y = y;
  };
};
class ColorPoint extends Point {
  constructor(x, y, color) {
    //未使用super
    this.color = color;
  };
};

new ColorPoint(10,10,"dog")//报错

(2) 另一个需要注意的地方是,在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。理由同上

class Point {
  constructor(x,y){
    this.x = x;
    this.y = y;
  };
};
class ColorPoint extends Point {
  constructor(x, y, color) {
    this.color = color;
    //调用不对
    super(x,y)
  };
};

new ColorPoint(10,10,"dog")//报错

(3) 子类可以调用父类静态方法

class Point {
  static hello() {
    console.log('hello world');
  }
  constructor(x,y){
    this.x = x;
    this.y = y;
  };
};


class ColorPoint extends Point {
  constructor(x, y, color) {
    super(x, y); // 调用父类的constructor(x, y)
    this.color = color;
  };
};

这是因为 extends 会导致子类ColorPoint构造函数与父类Point构造函数在一条原型链上。

12.3 与ES5继承的共同点

实例判断一致

dog instanceof ColorPoint // true
dog instanceof Point // true

12.4 super 关键字

super虽然代表了父类A的构造函数,但是返回的是子类B的实例,即super内部的this指的是B的实例,因此super()在这里相当于A.call(this)。

class A {}

class B extends A {
  constructor() {
    super();//A.call(this)。
  }
}

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

class A {}

class B extends A {
  m() {
    super(); // 报错
  }
}

(2)作为对象时,super 在普通方法中,指向父类的原型对象;在静态方法中,指向父类。

上面有提到static会使方法存在于构造函数,也就是说可以把 super 看作 隐式原型。

但是,通过super来给属性赋值是不被允许的,效果等于super.x === this.x

class A {
  constructor() {
    this.x = 1;
  }
}

class B extends A {
  constructor() {
    super();
    this.x = 2;
    super.x = 3;
    console.log(super.x); // undefined
    console.log(this.x); // 3
  }
}

let b = new B();

(3)super作为对象,用在 static 静态方法之中,这时super将指向父类,而不是父类的原型对象。

class Parent {
  static myMethod(msg) {
    console.log('static', msg);
  }

  myMethod(msg) {
    console.log('instance', msg);
  }
}

class Child extends Parent {
  static myMethod(msg) {
    super.myMethod(msg);
  }

  myMethod(msg) {
    super.myMethod(msg);
  }
}

Child.myMethod(1); // static 1

var child = new Child();
child.myMethod(2); // instance 2

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

class A {
  constructor() {
    this.x = 1;
  }
  static print() {
    console.log(this.x);
  }
}

class B extends A {
  constructor() {
    super();
    this.x = 2;
  }
  static m() {
    super.print();
  }
}

B.x = 3;
B.m() // 3

(5)注意,使用super的时候,必须显式指定是作为函数、还是作为对象使用,否则会报错。

class A {}

class B extends A {
  constructor() {
    super();
    //console.log(super); // 报错
    console.log(super.valueOf() instanceof B); // true
  }
}

let b = new B();

上面代码中,super.valueOf()表明super是一个对象,因此就不会报错。同时,由于super使得this指向B的实例,所以super.valueOf()返回的是一个B的实例。

(6)最后,由于对象总是继承其他对象的,所以可以在任意一个对象中,使用super关键字。

var obj = {
  toString() {
    return "MyObject: " + super.toString();
  }
};

obj.toString(); // MyObject: [object Object]

文章作者: iamfugui
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 iamfugui !
评论
  目录