ES6 Class类,就是构造函数语法糖?

860 阅读4分钟

一、Class 类可以看作是构造函数的语法糖

ES6引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。constructor()方法,这就是构造方法,而this关键字则代表实例对象。类的所有方法都定义在类的prototype属性上面,方法前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。使用的时候,类必须使用new调用跟构造函数的用法完全一致。

  • 类不存在变量提升
    class Point {
      constructor(x, y) {
        this.x = x;
        this.y = y;
      }

      toString() {
        return '(' + this.x + ', ' + this.y + ')';
      }
    }
    var p = new Point(1, 2);

通过代码证明:

    class Point {
      // ...
    }

    typeof Point // "function"
    Point === Point.prototype.constructor // true

类的数据类型就是函数,类本身就指向构造函数。

constructor: 方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor()方法,如果没有显式定义,一个空的constructor()方法会被默认添加。

class Point {
}

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

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

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

            get nl() {
                return this.age
            }

            set nl(value) {
                this.age = value
            }
        }
        let p = new Person('fzw', 25)
        console.log(p.nl);
        p.nl = 44
        console.log(p.nl);

class表达式

        let person = new class {
            constructor(name) {
                this.name = name;
            }

            sayName() {
                console.log(this.name);
            }
        }('张三');

        person.sayName(); // "张三"
        

上面代码中,person是一个立即执行的类的实例。

二、静态方法、静态属性

类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

         class Foo {
            static classMethod() {
                this.baz(); // 'hello'
                return '我被调用了';
            }
            static baz() {
                console.log('hello');
            }
            baz() {
                console.log('world');
            }
        }

        console.log(Foo.classMethod()); // 我被调用了

        var foo = new Foo();
        foo.classMethod() // TypeError: foo.classMethod is not a function

注意 如果静态方法包含this关键字,这个this指的是类,而不是实例。静态方法可以与非静态方法重名。

        class Foo {
            static classMethod() {
                return 'hello';
            }
        }

        class Bar extends Foo {
        }

        Bar.classMethod() // 'hello'

父类的静态方法,可以被子类继承。父类Foo有一个静态方法,子类Bar可以调用这个方法。 静态方法也是可以从super对象上调用的。

        class Foo {
            static classMethod() {
                return 'hello';
            }
        }

        class Bar extends Foo {
            static classMethod() {
                // super在静态方法之中指向父类
                return super.classMethod() + ', too';
            }
        }

        console.log(Bar.classMethod());

注意 super 在静态方法之中指向父类。

静态属性

static 关键词修饰,可继承使用

         class MyClass {
            static myStaticProp = 42;
            constructor() {
                console.log(MyClass.myStaticProp); // 42
            }
        }
        class Bar extends MyClass {
        }
        new MyClass()
        console.log(Bar.myStaticProp);

三、私有方法和私有属性

#修饰属性或方法,私有属性和方法只能在类的内部使用。 私有属性也可以设置 getter 和 setter 方法 私有属性和私有方法前面,也可以加上static关键字,表示这是一个静态的私有属性或私有方法。

    class Counter {
      #xValue = 0;

      constructor() {
        console.log(this.#x);
      }

      get #x() { return this.#xValue; }
      set #x(value) {
        this.#xValue = value;
      }
    }

四、class 继承

  • Class 可以通过extends关键字实现继承,让子类继承父类的属性和方法。
  • ES6 规定,子类必须在constructor()方法中调用super(),如果不调用super()方法,子类就得不到自己的this对象。调用super()方法会执行一次父类构造函数。
  • 在子类的构造函数中,只有调用super()之后,才可以使用this关键字,
        class Foo {
            constructor() {
                console.log(1);
            }
        }

        class Bar extends Foo {
            constructor(color) {
                // this.color = color; // ReferenceError
                super();
                this.color = color; // 正确
            }
        }

        const bar = new Bar('blue');
        console.log(bar); // Bar {color: 'blue'}

super 关键字

super这个关键字,既可以当作函数使用,也可以当作对象使用。

  • super作为函数调用时,代表父类的构造函数。只能用在子类的构造函数之中,用在其他地方就会报错。
  • super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。

作为对象,普通方法中super指向父类的原型对象

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

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

    let b = new B();
    b.m() // 2

注意: 在子类普通方法中通过super调用父类的方法时,方法内部的this指向当前的子类实例。

作为对象,静态方法之中,这时super将指向父类

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

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

        class Child extends Parent {
            static myMethod(msg) {
                // super 代表父类
                super.myMethod(msg);
            }

            myMethod(msg) {
                // super 代表父类原型对象
                super.myMethod(msg);
            }
        }

        Child.myMethod(1); // static 1

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

extends 关键字

168fb9a3828f9cb4_tplv-t2oaga2asx-zoom-in-crop-mark_4536_0_0_0.awebp
    // 1、构造器原型链
    Child.__proto__ === Parent; // true
    Parent.__proto__ === Function.prototype; // true
    Function.prototype.__proto__ === Object.prototype; // true
    Object.prototype.__proto__ === null; // true
    // 2、实例原型链
    child.__proto__ === Child.prototype; // true
    Child.prototype.__proto__ === Parent.prototype; // true
    Parent.prototype.__proto__ === Object.prototype; // true
    Object.prototype.__proto__ === null; // true

extends 继承,主要就是:

  1. 把子类构造函数(Child)的原型(__proto__)指向了父类构造函数(Parent),
  2. 把子类实例child的原型对象(Child.prototype) 的原型(__proto__)指向了父类parent的原型对象(Parent.prototype)。

这两点也就是图中用不同颜色标记的两条线。

子类构造函数Child继承了父类构造函数Parent的里的属性,使用super调用的。