前言
在ES5中,生成实例对象的传统方法是通过构造函数,如:
function Rectangle (height, width) {
this._height = height;
this._width = width;
}
Rectangle.prototype.calculate = function () {
return this._height * this._width;
};
const rect1 = new Rectangle(1, 2);
rect1.calculate(); // 2
在ES6中,class (类)是用于创建对象的模板,可以通过 class 关键字定义类。
class 的本质是 function。
它可以看作一个语法糖,让对象原型的写法更加清晰、更像面向对象编程的语法。
class Rectangle {
constructor(height, width) {
this._height = height;
this._width = width;
}
calculate() {
return this._height * this._width;
}
}
const rect2 = new Rectangle(2, 2);
rect2.calculate(); // 4
typeof Rectangle // "function"
基础用法
类声明
- 用带有
class关键字的类名进行声明 - 对类使用
new命令生成实例对象
类表达式
类表达式可以命名或不命名,可以通过类的 (而不是一个实例的) name 属性来检索它。
// 未命名/匿名类
let Rectangle = class {
constructor(height, width) {
this._height = height;
this._width = width;
}
};
console.log(Rectangle.name); // "Rectangle"
// 命名类
let Rectangle = class Rectangle2 {
constructor(height, width) {
this._height = height;
this._width = width;
}
};
console.log(Rectangle.name); // "Rectangle2"
类声明和类表达式不会提升,首先需要声明,然后再访问,否则抛错ReferenceError
主体
属性
prototype属性
ES6 中,prototype 仍旧存在,虽然可以直接在类中定义方法,但是其实方法还是定义在 prototype 上的。因此,在类的实例上面调用方法,其实就是调用原型上的方法。
class Rectangle {}
const rect = new Rectangle();
rect.constructor === Rectangle.prototype.constructor // true
name属性
返回跟在 class 后的类名(存在时)。
实例属性
在constructor()方法里面的this上面定义
class Rectangle {
constructor(height, width) {
this._height = height;
this._width = width;
}
}
静态属性
静态属性指的是 Class 本身的属性, 即Class.propname, 而不是定义在实例对象( this) 上的属性,而是定义在类的外面
Rectangle.staticWidth = 20;
ES6 明确规定, Class 内部只有静态方法, 没有静态属性。
ES7 有一个静态属性的提案, 目前 Babel 转码器支持:
- 实例属性
ES6中定义实例属性, 只能写在类的constructor方法里面。
class ReactCounter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
}
ES7中, 可以这样写:
class ReactCounter extends React.Component {
state = {
count: 0
};
}
为了增强可读性, 对于那些在constructor里面已经定义的实例属性,还可以这样:
class ReactCounter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
state;
}
- 静态属性
ES6的静态属性定义在类的外部,很容易被忽略。
ES7中, 可以这样写:
class Rectangle {
static staticWidth = 20;
constructor(height, width) {
this._height = height;
this._width = width;
}
}
方法
constructor方法
constructor方法是类的默认方法,创建类的实例化对象时被调用。constructor方法默认返回实例对象(即this),也可以指定返回另外一个对象。
继承
extends
- 子类使用
extends关键字实现继承
super
- 子类中使用
super关键字来调用父类的构造函数和方法
继承的 .prototype 必须是一个 Object 或者 null。
class Car {
constructor(brand) {
this.carname = brand;
}
present() {
return 'I have a ' + this.carname;
}
}
class Model extends Car {
constructor(brand, mod) {
super(brand);
this.model = mod;
}
show() {
return this.present() + ', it is a ' + this.model;
}
}
const mycar = new Model("Ford", "Mustang");
存取器
getter 与 setter
getter不可单独出现getter与setter必须同级出现getter与setter是设置在属性的Descriptor对象上的
class CustomHTMLElement {
constructor(element) {
this.element = element;
}
get html() {
return this.element.innerHTML;
}
set html(value) {
this.element.innerHTML = value;
}
}
var descriptor = Object.getOwnPropertyDescriptor(
CustomHTMLElement.prototype, "html"
);
"get" in descriptor // true
"set" in descriptor // true
以上代码的 getter 与 setter 是定义在html属性的描述对象上的。
修饰符
访问修饰符(Access Modifiers): public、private 和 protected
public修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有属性和方法都是publicprivate修饰的属性或方法是私有的,只能在声明它的类的内部访问protected修饰的属性或方法是受保护的,能在类和类的子类使用
readonly
使用 readonly关键字设置只读属性,必须在声明时或构造函数里被初始化。
class Animal {
readonly name;
public constructor(name) {
this.name = name;
}
}
const a = new Animal('Jack');
console.log(a.name); // Jack
a.name = 'Tom'; // error
static
使用 static 修饰符修饰的属性称为静态属性,只能在类中使用
使用 static 修饰符修饰的方法称为静态方法,它们不需要实例化,而是直接通过类来调用
abstract
abstract 用于定义抽象类和其中的抽象方法。
抽象类
- 抽象描述一种概念,做为其它类的基类使用
- 无法创建抽象类的实例,抽象类只能被继承
抽象方法
- 抽象方法不能在抽象类中实现,只能在抽象类的具体子类中实现,而且必须实现
- 抽象方法只能出现在抽象类中
abstract class Animal {
public name;
public constructor(name) {
this.name = name;
}
public abstract sayHi();
}
const a = new Animal('Jack'); // error
class Cat extends Animal {
public eat() {
console.log(`${this.name} is eating.`);
}
}
const cat = new Cat('Tom'); // error: Non-abstract class 'Cat' does not implement inherited abstract member 'sayHi' from class 'Animal'.
上面的例子中,Cat 继承了抽象类 Animal,但是没有实现抽象方法 sayHi,编译报错。
如何正确使用抽象类:
abstract class Animal {
public name;
public constructor(name) {
this.name = name;
}
public abstract sayHi();
}
class Cat extends Animal {
public sayHi() {
console.log(`Meow, My name is ${this.name}`);
}
}
const cat = new Cat('Tom');
cat.sayHi(); // Meow, My name is Tom
类的类型
给类加上 TypeScript 的类型:
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
sayHi(): string {
return `My name is ${this.name}`;
}
}
let a: Animal = new Animal('Jack');
console.log(a.sayHi()); // My name is Jack
有个疑问:
用 private 和 static 修饰的属性,有区别吗?
参考: