Class关键字
类的声明
类是什么
ECMAScript 6提供了更接近传统开发语言的写法,引人了类(Class )的概念。类作为对象的模板,只是一个语法糖。class关键字只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
// ECMAScript 5使用构造函数定义
function Point(x, y) { this.x = x; this.y = y; }
Point.prototype.toString = function () {
return '(' + this.x +', '+this.y + ')";
};
// ECMAScript 6使用类的概念定义
class Point {
constructor(x, y) {
this.x = x;
this.y =y;
}
toString(){
return '(' + this.x + ', ' + this.y + ')";
}
}
类的声明
声明类具有以下两种方式:
-
类的声明方式
class name [extends] { // class body }
- name:表示当前声明的类名。
但是不同于类表达式,类声明不允许再次声明已经存在的类,否则将会抛出一个类型错误。
- name:表示当前声明的类名。
-
类的表达式方式
const MyClass = class [className][extends]{ // class body };
和函数表达式相同的一点是,类表达式可以是命名也可以是匿名的。如果是命名类表达式,这个名字只能在类体内部才能访问到。
构造函数
构造函数(Constructor )是用于创建和初始化类中创建的一个对象的一种特殊方法。
constructor([arguments]){... }
- 在一个类中只能有一个名为“constructor”的特殊方法。一个类中出现多次构造函数(Constructor)方法将会抛出一个SyntaxError错误。
- 在一个构造方法中可以使用super关键字来调用一个父类的构造方法。
- 如果没有显式指定构造方法,则会添加默认的constructor方法。
- 如果不指定一个构造函数( constructor )方法,则使用一个默认的构造函数(constructor ) 。
getter与setter
与ECMAScript 5一样,在“类”的内部可以使用get和 set 关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。
class MyClass {
constructor() {
// ...
}
get prop() {
return 'getter";
}
set prop(value) {
console.log('setter: ' + value);
}
let inst = new MyClass();
inst.prop = 123;// setter: 123
console.log(inst.prop);// 'getter'
不允许声明提前
声明类时,是不存在声明提前的现象的。如下示例代码所示:
new Foo(); // ReferenceError: Foo is not defined
class Foo {}
上逑代码示例中,Foo类调用在前,声明在后。由于 ECMAScript 6不允许类的声明提前,结果为报错。
这种规定的原因与继承有关,必须保证子类在父类之后定义。
不允许重复声明
声明类时,是不存在重复声明的。如果一个类被重复声明的话,则引起解析错误。如下示例代码所示:
class Foo {};
class Foo {};// SyntaxError: ldentifier 'Foo' has already been declared
若之前使用类表达式定义了一个类,则再次声明这个类同样会引起解析错误。
let Foo = class {};
class Foo {};// SyntaxError: ldentifier 'Foo' has already been declared
静态方法
静态方法的语法
static关键字为一个类定义了一个静态方法。静态方法不会在类的实例上被调用,相反被类本身调用。
static methodName() { ... }
- methodName:表示指定类中的静态方法名称。
静态方法调用直接在类上进行,不能在类的实例上调用。
class ClassWithStaticMethod {
static staticMethod(){
return 'static method has been called.';
}
}
console.log(ClassWithStaticMethod.staticMethod());
// "static method has been called."
静态方法的调用
- 从另一个静态方法调用
在同一个类中的一个静态方法调用另一个静态方法,可以使用this关键字。
class StaticMethodCall {
static staticMethod() {
return 'Static method has been called';
}
static anotherStaticMethod() {
return this.staticMethod() + ' from another static method';
}
StaticMethodCall.staticMethod();
// 'Static method has been called'
StaticMethodCall.anotherStaticMethod();
// 'Static method has been called from another static method'
-
从类的构造函数和其他方法调用
静态方法不能直接在非静态方法中使用this关键字来访问。- 需要使用类名来调用:CLASSNAME.STATIC_METHOD_NAME()
- 将其作为构造函数的属性来调用该方法: this.constructor.STATICMETHOD.NAME().。
class StaticMethodCall { constructor() { console.log(StaticMethodCall.staticMethod());// 'static method has been called.' console.log(this.constructor.staticMethod());// 'static method has been called.' } static staticMethod() { return 'static method has been called.'; } }
类的继承
实现类的继承
extends关键字用于类声明或者类表达式中,以创建一个类,该类作为另一个类的子类。
class ChildClass extends ParentClass { ... }
extends 关键字用来创建一个普通类或者内建对象的子类。
值得注意的是,继承的.prototype必须是一个Object或者null
- 编写一个类作为父类,如下示例代码所示:
class Polygon {
constructor(height, width){
this.name = 'Polygon';
this.height = height;
this.width = width;
}
sayName(){
console.log('Hi, l am a ' , this.name + '.');
}
}
- 编写一个类作为Polygon类的子类,如下示例代码所示:
class Square extends Polygon {
constructor(length) {
super(length, length);
this.name = 'Square';
}
get area() {
return this.height * this.width;
}
set area(value) {
this.area = value;
}
}
let s = new Square(5);
s.sayName();// Hi, l am a Square.
继承于内置对象
如下示例代码所示,实现了继承了内置的Date 对象。
class myDate extends Date {
constructor() {
super();
}
getFormattedDate() {
var months = [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
];
return (
this.getFullYear() + "-" + months[this.getMonth()] + "-" + this.getDate()
);
}
}
var mydate = new myDate();
console.log(mydate.getFormattedDate());
super关键字
super 关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同。
- super关键字作为函数调用时,代表父类的构造函数。ECMAScript 6要求子类的构造函数必须执行一次 super 函数。
class A {}
class B extends A {
constructor(){
super();
}
}
值得注意的是,super虽然代表了父类A的构造函数,但是返回的是子类B的实例,即super内部的this指的是B,因此 super()在这里相当于A.prototype.constructor.call(this)。
-
super 作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
class A { p() { return 2; } } var a = new A(); a.c = "C"; class B extends A { constructor() { super(); } } var b = new B(); console.log(b.c); // undefined
值得注意的是,由于super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的。
类的完整示例:
class Parent {
constructor(name) {
this.name = name;
}
// 当前类的(实例对象)方法,而不是当前类的原型方法
toString() {
return "this is parent method.";
}
static staticMethod() {
return "this is parent method.";
}
}
let parent = new Parent("东方月初");
// parent.toString(); // this is parent method.
// Parent.prototype.toString(); // this is parent method.
// parent.__proto__.toString(); // this is parent method.
class Child extends Parent {
constructor(name, age) {
super(name); // 指向父类的构造器
this.age = age;
// console.log(super.toString());
}
toString() {
// super -> 指向父类的原型对象(Parent.prototype)
return "this is child method." + super.toString();
}
static staticMethod() {
// super -> 指向父类(Parent)
return "this is child method." + super.staticMethod();
}
}
let child = new Child("白月初", 18);
// console.log(child.toString());
// console.log(Child.staticMethod());