Class 语法

1. 定义类

JavaScript 中,定义构造函数与普通函数并无差别,这种写法跟传统的面向对象语言(比如 C++ 和 Java)差异很大,很容易让新学习这门语言的程序员感到困惑。

ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过 class 关键字,可以定义类。

//定义类
class Person {
  //把所有的属性在这里声明 属性会添加到实例上
  name = null;
    age = null;

  //定义构造方法 实例化的时候自动执行
  constructor(name, age=10) {
    this.name = name;
    this.age = age;
  }

  // 定义方法,会添加到原型上
  say() {
    console.log('MY Name is '+this.name);
  };

  eat() {
    console.log('My age is '+this.age);
  }
}


console.log(typeof Person);  // function
Person(); // 不能被调用,会报错

// 实例化
let p = new Person();
p instanceof Person; // true;

上面方式定义的类,具有如下特点:

1)ES6 的类,本质上还是构造函数,使用 typeof 查看类的类型,返回function

2)类可以被实例化,但是不能被调用,这是与之前的构造函数的主要区别。

3)上述代码中定义的属性,会添加到类的实例上。

4)上述代码中定义的方法,会添加到类的实例的原型上。

5)constructor 被称之为构造方法,会在实例化的时候自动调用,可用于对属性初始化赋值。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。

6)类中的方法内使用 this,它默认指向类的实例,任然遵循谁调用指向谁的规则。

7)类中可以定义属性和方法,但不能写其他语句,否则会报错;当然,方法的里面就可以写任何的语句了。

2 静态方法

方法前面加static 关键字,就表示该方法不会添加到实例的原型上,而是添加到类本身上,被称为静态方法

如果静态方法包含 this 关键字,这个 this 默认指向的是类本身,任然遵循谁调用指向谁的规则。

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

Foo.classMethod() // 'hello'

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

注意:

在属性名前面添加 static 关键字也可以创建静态属性,也就是给类本身添加属性,但这个语法还在提案中,还处于 Stage3 阶段!

3 定义访问器属性

class 语法可以直接添加访问器属性,不用像 ES5 那样使用 Object.defineProperty() 方法。

下面代码添加了访问器属性 fullName。

//定义类
class Person {
  //定义属性
  firstName = '尼古拉斯';
    lastName = '赵四';

  //当获取fullName属性值的时候 自动调用
  get fullName() {
    return this.firstName + '·' + this.lastName;
  }

  //当设置fullName属性值的时候 自动调用  接受一个参数,是要给fullName属性设置的新值
  set fullName(val) {
    this.firstName = val.split('·')[0];
    this.lastName = val.split('·')[1];
  }
}

4 Class 实现继承

4.1 使用 extends 关键字实现继续

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

class Point {
}

class ColorPoint extends Point {
}

let p = new Point();
let cp = new ColorPoint();

cp.__proto__ instanceof Point; // true
ColorPoint.__proto__ === Point;  // true

上面代码中,ColorPoint 这个类就继承了 Point 这个类,我们把 ColorPoint 称为子类,Point 称为父类

继承之后,如有如下特点:

1)子类的实例的原型会指向父类的一个实例,所以子类的实例会继承父类实例的属性和方法。

2)子类本身的原型会指向父类,所以子类也会继承父类的静态方法。

注意:一个父类可以有多个子类,但一个子类只能有一个父类。

4.2 方法和属性的重写

子类中如果定义了与父类中同名的属性和方法,那么子类的实例就会按照子类中定义的来。我们称为方法重写属性重写

class Point {
  name = 'Point';
    age = 100;

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

  sleep() {
    console.log('Point sleep');
  }

}

class ColorPoint extends Point {
  name = 'Color Point';
  grade = '115';

  eat() {
    console.log('ColorPoint eat');
  }

  say() {
    console.log('ColorPoint say');
  }
}


let cp = new ColorPoint();

console.log(cp);  // {name: "Color Point", age: 100, grade: "115"}
cp.sleep(); // Point sleep
cp.say(); // ColorPoint say

4.3 super关键字

前面我们学到过,super 这个关键字与 this 是相对的,super 指向方法所属对象的原型。

除此之外,在子类的构造方法中,super 可以作为函数调用,代表父类的构造方法;子类重写父类构造方法的时候通过调用 super 可以把父类构造方法中的代码执行一遍。

ES6 要求,子类的构造函数必须执行一次 super 函数。

class A {}

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

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

4.4 继承原生构造函数

ES6 允许继承原生构造函数定义子类

class MyArray extends Array {
  constructor(...args) {
    super(...args);
  }
}

var arr = new MyArray();
arr[0] = 12;
arr.length // 1

results matching ""

    No results matching ""