前言
JavaScript在 ES6 引入的类的概念无疑让这门语言拥有了更贴近于传统面向对象编程语言的编写模式
类的声明
ES6引入了 class 关键字来进行类的声明,类的声明可以直接使用class关键字和类名组合声明,也可以是使用表达式的形式声明。
1、类声明和即将要讲解的类表达式都不会被提升;
2、类中的代码在执行时,会强制开启严格模式;
3、类的所有方法都不可枚举,并且不能与new组合使用。
使用class关键字和类名直接声明
// 在ES5及以前中,没有类的概念,通过构造函数和原型混合模拟定义‘类’,转化一下的写法如下
// function Person(aAge, aName) {
// this.age = aAge;
// this.name = aName;
// }
// Person.prototype.showName = function () {
// console.log(this.name);
// }
// 类的声明
class Person {
// 构造函数
constructor(aAge, aName) {
this.age = aAge;
this.name = aName;
}
// 等同于Person.prototype.showName
showName(){
console.log(this.name);
}
}
// 执行输出:Bob
let person = new Person(18, 'Bob');
person.showName();
表达式声明
let Person = class {
// 构造函数
constructor(aAge, aName) {
this.age = aAge;
this.name = aName;
}
// 等同于Person.prototype.showName
showName(){
console.log(this.name);
}
}
- 每个类有且只有一个构造函数:constructor(),如果未显式声明,则默认一个空的构造函数以供调用(与C++类似)。
- 类的使用consturctor方法名字定义构造函数
- 自有(私有)属性和方法一般在consturctor中定义
类的成员
类的成员包含普通的原型方法或自有属性,还有特殊功能的构造函数、生成器、静态方法和访问器属性等,并且成员名可以是表达式。
自有属性
类中的自有属性一般在构造函数中执行初始化。
class People {
constructor() {
this.name = "Bob";
}
}
访问器属性
访问器属性通过关键字 get 和 set 创建,可以用来控制属性的读写。
class Person {
constructor(aAge, aName) {
this._age = aAge;
this._name = aName;
}
showName() {
console.log(this.name);
}
// age 可以读写
get age() {
return this._age;
}
set age(value) {
this._age = value;
}
// name只读
get name() {
return this._name;
}
}
let person = new Person(18, 'Bob');
console.log(person.age); // 18
person.age = 20;
console.log(person.age);// 20
console.log(person.name); // Bob
person.name = 'CP';
console.log(person.name);// Bob
静态方法
ES6新增了static关键字,可把类中的方法(除了构造函数)定义成静态的,注意ES6无静态属性,只有静态方法。
class Person {
// 构造函数
constructor(aAge, aName) {
this.age = aAge;
this.name = aName;
}
static sTest(){
console.log('sTest');
}
}
Person.sTest(); // sTest
类的继承
ES6提供了 extends 关键字实现类的继承。
代码示例:Student类通过关键字 extends 继承Person类,Student类称为派生类,Person类称为基类。
class Person {
constructor(aAge, aName) {
this.age = aAge;
this.name = aName;
}
run() {
console.log(this.name + ' is running...');
}
}
class Student extends Person{
constructor(aAge, aName, aId){
super(aAge, aName);
this.id = aId;
}
}
let student = new Student(18, 'Bob','001');
student.run(); //Bob is running...
- 派生类定义了构造函数,则必需且需要在this之前调用super();
- 派生类没有定义构造函数,在派生类实例化的时候实例会自动调用super()并传入所有参数;
class Person {
constructor(aAge, aName) {
this.age = aAge;
this.name = aName;
}
run() {
console.log(this.name + ' is running...');
}
}
class Teacher extends Person {
// 无构造函数
}
// 等价于
// class Teacher extends Person{
// constructor(...args){
// super(...args)
// }
// }
let teacher = new Teacher(28, 'CP', '002');
teacher.run(); // CP is running...
- 派生类中可以重写基类的方法
class Person {
constructor(aAge, aName) {
this.age = aAge;
this.name = aName;
}
run() {
console.log(this.name + ' is running...');
}
}
class Student extends Person {
constructor(aAge, aName, aId) {
super(aAge, aName);
this.id = aId;
}
// 覆盖Person.prototype.run()方法
run() {
console.log(this.id + this.name + ' is running...');
}
}
let student = new Student(18, 'Bob', '001');
student.run();// 001Bob is running...
- 派生类中可以用super调用基类方法
class Person {
constructor(aAge, aName) {
this.age = aAge;
this.name = aName;
}
run() {
console.log(this.name + ' is running...');
}
}
class Student extends Person {
constructor(aAge, aName, aId) {
super(aAge, aName);
this.id = aId;
}
study() {
super.run();
console.log(this.name + ' is studying...');
}
}
let student = new Student(18, 'Bob', '001');
student.study();
//Bob is running...
//Bob is studying...