CodeAshen's blog CodeAshen's blog
首页
  • Spring Framework

    • 《剖析Spring5核心原理》
    • 《Spring源码轻松学》
  • Spring Boot

    • Spring Boot 2.0深度实践
  • Spring Cloud

    • Spring Cloud
    • Spring Cloud Alibaba
  • RabbitMQ
  • RocketMQ
  • Kafka
  • MySQL8.0详解
  • Redis从入门到高可用
  • Elastic Stack
  • 操作系统
  • 计算机网络
  • 数据结构与算法
  • 云原生
  • Devops
  • 前端
  • 实用工具
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
  • Reference
GitHub (opens new window)

CodeAshen

后端界的小学生
首页
  • Spring Framework

    • 《剖析Spring5核心原理》
    • 《Spring源码轻松学》
  • Spring Boot

    • Spring Boot 2.0深度实践
  • Spring Cloud

    • Spring Cloud
    • Spring Cloud Alibaba
  • RabbitMQ
  • RocketMQ
  • Kafka
  • MySQL8.0详解
  • Redis从入门到高可用
  • Elastic Stack
  • 操作系统
  • 计算机网络
  • 数据结构与算法
  • 云原生
  • Devops
  • 前端
  • 实用工具
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
  • Reference
GitHub (opens new window)
  • CSS3浮动定位与背景样式

  • CSS动画

  • JS基础语法与表达式

  • 流程控制语句与数组

  • JS函数与DOM

  • 面向对象

  • 正则表达式

  • ES6基础入门

  • ES6语法扩展

  • Promise与Class

    • 01-Promise
    • 02-Class类
      • Class 是什么
      • Class 的两种定义形式
      • 实例属性和方法
      • 静态方法
      • 静态属性
      • 私有属性和方法
      • extends 关键字
      • super 关键字
        • 作为函数使用
        • 作为对象使用
  • Module与Babel

  • 前端
  • Promise与Class
CodeAshen
2023-02-10
目录

02-Class类

# 初识 Class

# Class 是什么

类可以看做是对象的模板,用一个类可以创建出许多不同的对象。

class Person {
  // 实例化时执行构造方法,所以必须有构造方法,但可以不写出来
  constructor(name, age) {
    // console.log('实例化时执行构造方法');
    // this 代表实例对象,上面定义的是实例属性/方法
    this.name = name;
    this.age = age;
  }
  
  // 各实例共享的方法
  speak() {
    console.log('speak');
  }
}

const zs = new Person('ZS', 18);
const ls = new Person('LS', 28);
zs.speak();
console.log(zs.speak === ls.speak);  // true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

可以发现 Class 和我们之前学习的构造函数类似。下面看回顾下构造函数的写法,对比一下:

function Person(name, age) {
  this.name = name;
  this.age = age;
}

Person.prototype.speak = function () {
  console.log('speak');
};
1
2
3
4
5
6
7
8

之前学的构造函数用来模拟类,现在学习了 Class 就可以不用构造函数来做这个事情了。其实本质上还是构造函数那一套:

Class Person {}
cosole.log(typeof Person);  // function
1
2

# Class 的两种定义形式

Class 的定义有两种形式:声明形式和表达式形式。

  1. 声明形式
class Person {
  constructor() {}

  speak() {}
}
1
2
3
4
5
  1. 表达式形式

先回顾下构造函数的声明形式和表达式形式:

// 构造函数声明形式
function Person(){}

// 构造函数表达式形式
const Person = function () {};
1
2
3
4
5

Class 的表达式形式定义:

const Person = class {
  constructor() {
    console.log('constructor');
  }
  speak() {}
};

new Person();
1
2
3
4
5
6
7
8

立即执行函数:

(function () {
  console.log('func');
})();
1
2
3

立即执行匿名类:

new (class {
  constructor() {
    console.log('constructor');
  }
})();
1
2
3
4
5

# Class 的属性和方法

# 实例属性和方法

实例属性和实例方法需要通过类的实例来访问。

class Person {
  // 实例属性
  age = 0;
  sex = 'male';
  // 实例方法:方法就是值为函数的特殊属性
  getSex = function () {
    return this.sex;
  };

  // 构造方法访问实例属性
  constructor(name, sex) {
    this.name = name;
    this.sex = sex;
  }
  
  // 实例方法
  speak() {
    console.log(`I'm ${this.name}.`)
  }
}

const p = new Person('Alex');
// 通过实例 p 访问 实例属性
console.log(p.name);
console.log(p.age);
// 通过实例 p 访问实例方法 speak
p.speak();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 静态方法

静态方法就是类的方法,不需要实例化类,就可以调用静态方法。

class Person {
  // 实例方法
  speak() {
    console.log('speak');
    console.log(this);  // 实例方法中的 this 指向实例对象
  }
  
  // 静态方法
  static speak() {
    console.log('Person speak');
    console.log(this);  // 静态方法中的 this 指向类
  }
}

// 通过实例调用实例方法
const p = new Person();
p.speak();  // speak

// 通过类名调用静态方法
Person.speak();  // Person speak

// 可以在 class 外为类添加静态方法
Person.sleep = function () {
  console.log('Person sleep');
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 静态属性

静态属性就是类的属性,不推荐在属性前加 static 作为静态属性,有兼容性问题。

class Person {
  // 不要这么写,目前只是提案,有兼容性问题
  static version = 1.0;
}
1
2
3
4

那就只能在 class 外部添加属性了。

Person.version = '1.0';
1

推荐写成静态方法的形式,通过静态方法获取静态属性。

class Person {
  static getVersion() {
    return '1.0';
  }
}
1
2
3
4
5

# 私有属性和方法

为什么需要私有属性和方法呢?一般情况下,类的属性和方法都是公开的,公有的属性和方法可以被外界修改,造成意想不到的错误。

有些方法和属性我们只希望在实例内部使用,不想暴露出去。这就需要我们实现私有属性和方法了。

ES6 并没有对私有属性和方法的支持,需要通过一些方式模拟私有属性和方法。下面介绍两种模拟方式。

方式一:_ 开头表示私有(软约束)

class Person {
  constructor(name) {
    this._name = name;
  }

  // 通过公开方法访问私有属性
  getName() {
    return this._name;
  }
}

const p = new Person('Alex');
console.log(p.getName());
1
2
3
4
5
6
7
8
9
10
11
12
13

方式二:将私有属性和方法移出类(强约束)

// 在函数作用域中定义属性和类,外界访问不到
(function () {
  // 属性不定义在 Person 类里面,而是定义在同级作用域中
  let name = '';

  class Person {
    constructor(username) {
      name = username;
    }

    getName() {
      return name;
    }
  }

  // 通过 window 对象将 Person 类暴露出去
  window.Person = Person;
})();


(function () {
  const p = new Person('Alex');
  console.log(p.name);       // undefined
  console.log(p.getName());  // Alex
})();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# Class 的继承

# extends 关键字

我们来演示继承,先编写父类 Person。

class Person {
  constructor(name, sex) {
    // 实例属性和方法
    this.name = name;
    this.sex = sex;
    this.say = function () {
      console.log('say');
    };
  }

  // 实例方法
  speak() {
    console.log('speak');
  }

  // 静态属性
  static speak() {
    console.log('static speak');
  }
}

// 静态属性
Person.version = '1.0';
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

使用 extends 关键字继承父类。

// 使用 extends 关键字继承父类
class Programmer extends Person {
  constructor(name, sex) {
    // 子类构造方法中必须使用 super 关键字调用父类构造方法
    super(name, sex);
  }
}

const zs = new Programmer('zs', '男');
console.log(zs.name);  // zs
console.log(zs.sex);   // 男
zs.say();    // say
zs.speak();  // speak
Programmer.speak();  // static speak
console.log(Programmer.version);  // 1.0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

可以看到子类继承了父类所有的属性和方法。

子类还可以改写父类的属性和方法,还可以添加属性和方法。

class Programmer extends Person {
  constructor(name, sex, feature) {
    // this 操作不能放在 super 前面
    super(name, sex);
    this.feature = feature;  // 添加实例属性
  }

  // 添加实例方法
  hi() {
    console.log('hi');
  }

  // 改写实例方法
  speak() {
    console.log('Programmer speak');
  }

  // 改写静态方法
  static speak() {
    console.log('Programmer static speak');
  }
}

// 覆盖静态变量
Programmer.version = '2.0';
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# super 关键字

super 关键字既可以作为函数使用,也可以作为对象使用。

# 作为函数使用

super 关键字作为函数使用只能调用父类的构造方法,只能在子类的构造方法中使用。

此时 super 虽然代表了父类的构造方法,但是内部的 this 指向子类的实例。

class Person {
  constructor(name) {
    this.name = name;
    console.log(this);
  }
}

class Programmer extends Person {
  constructor(name, sex) {
    // super 关键字调用父类构造方法
    super(name, sex);
  }
}

new Programmer();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 作为对象使用

(1) 在构造方法中使用或一般方法中使用

super 关键字作为对象使用用于通过 . 访问父类的属性和方法。

此时 super 代表父类的原型对象 Person.prototype,所以定义在父类实例上的方法或属性,是无法通过 super 调用的。

通过 super 调用父类的方法时,方法内部的 this 指向当前的子类实例。

class Person {
  constructor(name) {
    this.name = name;
    console.log(this);
  }

  speak() {
    console.log('speak');
    // console.log(this);
  }

  static speak() {
    console.log('Person speak');
    console.log(this);
  }
}


class Programmer extends Person {
  constructor(name, sex) {
    super(name, sex);
    super.speak();
      }

  speak() {
    super.speak();  // speak
    console.log('Programmer speak');
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

(2) 在静态方法中使用

super 关键字用在静态方法中指向父类,而不是父类的原型对象。

通过 super 调用父类的方法时,方法内部的 this 指向当前的子类,而不是子类的实例。

class Programmer extends Person {
  constructor(name, sex) {
    super(name, sex);
    super.speak();
      }

  speak() {
    super.speak();  // speak
    console.log('Programmer speak');
  }
  
  static speak() {
    super.speak();  // Person speak
    console.log('Programmer speak');
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
编辑 (opens new window)
上次更新: 2023/06/04, 12:34:19
01-Promise
01-Module模块

← 01-Promise 01-Module模块→

最近更新
01
第01章-RabbitMQ导学
02-10
02
第02章-入门RabbitMQ核心概念
02-10
03
第03章-RabbitMQ高级特性
02-10
更多文章>
Theme by Vdoing | Copyright © 2020-2023 CodeAshen | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式