ES6之Class(类)

Class 定义

ES6 中引入了 class(类) 的概念。 通过 class 关键字,我们可以定义
class 可以看做是一个 语法糖,它的绝大部分功能,都可以用 ES5 做到, 它只是让 对象原型(prototype) 的写法 更加清晰、更像面向对象编程 的语法而已。

类的本质还是一个函数,类就是构造函数的另一种写法。

function Person(){}
console.log(typeof Person); //function

class Person{}
console.log(typeof Person); //function

Class 写法

ES5定义构造函数,一般来说是这样:

function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.say = function () {
    return '我叫' + this.name + ',今年' + this.age + '岁';
}

let p1 = new Person('张三', 18);
console.log(p1.say()); // 我叫张三,今年18岁

ES6 引入了 Class(类)这个概念,提供了更接近传统语言的写法,上述代码用 ES6 实现就是:

class Person {
    // 构造方法,this代表实例对象
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    // 这里的方法都是定义在prototype原型对象上的方法
    say() { // 定义在原型上
        return '我叫' + this.name + ',今年' + this.age + '岁';
    }
}
let p1 = new Person('张三', 18);
let p2 = new Person('李四', 20);
console.log(p1.say()); // 我叫张三,今年18岁

// 类的所有方法都定义在类的prototype属性上面
console.log(p1.say === p2.say); //true

注意:

  1. constructor就是原来构造函数的写法,类中的 this 指向实例对象
  2. 定义类的方法(say(){})的时候,并不需要加逗号,也不需要加function。
  3. class不存在变量提升现象,必须要先声明后使用。必须先有类,才可以实例化
  4. 添加的私有属性都在构造函数中添加
  5. 类的所有方法都定义在类的prototype原型对象上

constructor

constructor 方法是类的构造方法

  1. 是一个默认方法,通过 new 命令创建对象实例时,自动调用该方法。
  2. this代表实例对象
  3. 一个类必须有 constructor 方法,如果没有显式定义,一个默认的 consructor 方法会被默认添加。所以即使你没有添加构造函数,也是会有一个默认的构造函数的,如下例:
// ES6
class Person {}

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

// ES5
function Person () {}

一般 constructor 方法返回实例对象 this ,但是也可以指定 constructor 方法返回一个全新的对象,让返回的实例对象不是该类的实例。


get 和 set

在 class 的内部可以使用 get 和 set 关键字,对某个属性设置 存值函数(setter) 和 取值函数(getter),拦截 该属性的 存取行为。

class Point {
    constructor() {
        // ...
    }
    get x() {
        console.log('getter');
    }
    set x(val) {
        console.log(`setter: ${val}`)
    }
}
const p = new Point();
p.x = 1; // setter: 1
p.x // getter

静态方法和属性

类是实例的原型,所有类的方法都会被实例继承。但是,如果在一个方法前面加上static关键字,就代表这是一个静态方法,这个 静态方法不会被实例继承,而是通过类直接调用

class Person {
    constructor() {}
    static say() { // 静态方法
        return 'Hello World';
    }
}

var p = new Person();

// 只能通过类直接调用
console.log(Person.say()); // 'Hello World'

// 静态方法是不可以被实例继承
console.log(p.say()); // 报错  p.say is not a function

继承

extends 关键字

Class 的继承是通过extends关键字实现的

class Cat extends Animal{}

这里的子类 Cat,通过 extends 关键字,继承了父类 Animal 所有的属性和方法

class Animal {
    constructor(name, age) {
        this.name = name
        this.age = age
    }
    sayName() {
        return "it's name is " + this.name
    }
}

class Cat extends Animal {
    constructor(name, age, color) {
        super(name, age) // super关键字调用父元素的方法
        this.color = color
    }
    sayColor() {
        return "the color is " + this.color
    }
}

var bosimiao = new Cat("bosi", 2, 'yellow');

console.log(bosimiao.sayName()); // it's name is bosi
console.log(bosimiao.sayColor()); // the color is yellow

super 关键字

  1. 在子构造函数中,必须用 super 方法,否则新建对象的时候会报错
  2. 子构造函数中,使用 this 前必须先用 super,否则会报错

这是和ES6的继承机制有关:先创建父类的实例对象,然后在构建子类的实例,再修改父类中的this对象

super 既可以充当函数,也可以充当对象

1. 当做函数使用
充当函数时,只能在子类的构造函数中使用,且必须放在第一行调用。
指向父类的构造函数,只有super调用之后,子类才可以使用 this 关键字,指向子类的实例对象

class A {}
class B extends A {
  constructor() {
    super();  // ES6 要求,子类的构造函数必须执行一次 super 函数,否则会报错。
  }
}

:在 constructor 中必须调用 super 方法,super 代表了父类 A 的构造函数,但是返回的是子类 B 的实例,即 super 内部的 this 指的是子类B ,因此 super() 在这里相当于 A.prototype.constructor.call(this, props)

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

class B extends A {
  constructor() {
    super();
  }
}

new A(); // A
new B(); // B

可以看到,在 super() 执行时,它指向的是 子类 B 的构造函数,而不是父类 A 的构造函数。也就是说,super() 内部的 this 指向的是子类

2. 当做对象使用
充当对象时,放在普通函数中,super 指向父类的原型 prototype

class A {
  c() {
    return 2;
  }
}	

class B extends A {
  constructor() {
    super();
    console.log(super.c()); // 2
  }
}

let b = new B();

上面代码中,子类 B 当中的 super.c(),就是将 super 当作一个对象使用。这时,super 在普通方法之中,指向 A.prototype,所以 super.c() 就相当于 A.prototype.c()。

通过 super 调用父类的方法时,super 会绑定子类的 this。

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

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

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

上面代码中,super.s() 虽然调用的是 A.prototytpe.s(),但是 A.prototytpe.s()会绑定子类 B 的 this,导致输出的是 2,而不是 1。也就是说,实际上执行的是 super.s.call(this)。

posted @ 2022-07-20 18:15  猫老板的豆  阅读(138)  评论(0编辑  收藏  举报