类语法(Classes)
目录:
基本定义语法
用法:class name { ... }
class People{
constructor(name, sex){
this.name = name;
this.sex = sex;
}
say(){
console.log(`hello! I am ${this.name}.`);
}
}
const kobe = new People('kobe', 'man');
kobe.say(); // hello! I am kobe.
继承语法
用法:class SubClass extends superClass {}
class Point2D{
constructor(x, y){
this.x = x;
this.y = y;
}
toString(){
return `(${this.x}, ${this.y})`;
}
}
class Point3D extends Point2D{
constructor(x, y, z){
super(x, y);
this.z = z;
}
toString(){
return `(${this.x}, ${this.y}, ${this.z})`;
}
}
ES2015 的继承语法同样可以将以前使用的构建函数模拟的类作为父类来继承,而并非只有 class 语法定义的类才可以使用。
function Cat(){
Cat.prototype.climb = function(){
return "I can climb";
}
Cat.prototype.yell = function(){
return "Meow";
}
}
class Tiger extends Cat{
yell(){
return "Aoh";
}
}
const tiger = new Tiger();
console.log(tiger.yell()); //Aoh
console.log(tiger.climb()); //I can climb
需要非常注意的是,如果一个子类继承了一个父类,那么在子类的 constructor 构造函数中必须使用 super 函数调用父类的构造函数后才能在子类的 constructor 构造函数中使用 this ,否则会报出 this is not defined 的错误。
class Foo{
}
class Bar extends Foo{
constructor(){
this.property = 1;
}
}
new Bar(); // ReferenceError: this is not defined
这个问题在除 constructor 构造函数以外的方法中并不会出现,即便在子类的构造函数中并没有调用 super 函数,在其他方法中依然可以调用 this 来指向当前实例。
Getter/Setter
const List = {
_array: [],
set new(value){
this._array.push(value);
},
get last(){
return this._array[0];
},
get value(){
return this._array;
}
}
List.new = 1;
List.new = 2;
List.new = 3;
console.log(List.last); // 1
console.log(List.value); // [1, 2, 3]
静态方法
用法:class Name { static fn() { ... } }
class People{
constructor(number){
this.number = number;
}
say(){
console.log(`hello! I am No.${this.number}`);
}
static extend(constructor, ..._args){
return class extends People{
constructor(...args){
super(..._args);
constructor.call(this, ...args);
}
}
}
}
const Player = People.extend(function(name){
this.name = name;
}, 24);
const kobe = new Player("Kobe");
kobe.say(); // hello! I am No.24
console.log(kobe.name); // Kobe
注意事项
ES6的类语法虽然可以与以前的原型一起使用,但是他们之间也是有一定区别的。
函数有两种定义方法:声明式和定义式。
// 声明式
function Foo(){}
// 定义式
const Bar = function (){}
而声明式的函数定义是可以被提升的(hoisting),即在声明式函数所在的作用域开始执行时,无论是否已经执行到这个函数的代码,都可以对这个函数进行访问和使用。
const val = (function(){
return fn();
function fn(){ // fn would be hoisted
return 'foobar'
}
})();
console.log(val); // foobar
这个特性同样适用于利用原型定义的类,但对于 ES6 的类语法来说这个特性是无效的。
const point = new Point(1, 2); // ReferenceError
class Point{
constructor(x = 0, y = 0){
this.x = x;
this.y = y;
}
}
这是因为从逻辑角度上看,类的继承必须是单向的,不可能出现 A 类继承于 B 类的同时 B 类也继承于 A 类的现象,这就意味着,父类必须在子类定义之前被定义。
遗憾和期望
- 不支持私有属性(private)。
- 不支持实例属性,但目前可以通过 Getter/Setter 实现。
- 不支持多重继承。
- 暂时没有类似协议(Protocol)和接口(Interface)等的概念。