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