13. 类

在此我们介绍 ECMAScript 6 的类。首先,我们将展示函数如何被用作类。
13.1. 脚本 [class-00]
以下脚本展示了一种不寻常的函数用法。在此,我们将函数作为对象使用。
'use strict';
// 函数可作为对象使用
// 一个空壳
function f() {
}
// 从外部为其赋值属性
f.prop1 = "val1";
f.show = function () {
console.log(this.prop1);
}
// f 的用法
f.show();
// 函数 g 作为类运行
function g() {
this.prop2 = "val2";
this.show = function () {
console.log(this.prop2);
}
}
// 使用 [new] 实例化该函数
new g().show();
注释
- 第 5-7 行:函数 f 的主体未定义任何属性;
- 第 9-12 行:从外部为函数 f 赋予属性;
- 第 14 行:调用函数(对象)f。请注意,这里不是写 [f()],而是简单地写 [f]。这是对象的表示法;
- 第 17-22 行:定义函数 [g],如同定义一个带有属性和方法的类;
- 第 24 行:函数 [g] 由 [new g()] 实例化;
执行结果
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\classes\class-00.js"
val1
val2
ES6引入了类概念,这使我们今后无需通过函数即可直接使用类。
13.2. 脚本 [class-01]
脚本 [class-01] 定义了一个类 [Personne]:
// 类
class Personne {
// 构造函数
constructor(nom, prénom, âge) {
this.nom = nom;
this.prénom = prénom;
this.âge = âge;
}
// 获取器和设置器
get nom() {
return this._nom;
}
set nom(value) {
this._nom = value;
}
get prénom() {
return this._prénom;
}
set prénom(value) {
this._prénom = value;
}
get âge() {
return this._âge;
}
set âge(value) {
this._âge = value;
}
// toString 转换为 JSON
toString() {
return JSON.stringify(this);
}
}
// 调用类
function main() {
const personne = new Personne("Poirot", "Hercule", 66);
console.log("personne=", personne.toString(), typeof (personne), personne instanceof (Personne));
}
// 调用 main
main();
注释
- 第 2 行:关键字 [class] 表示一个类;
- 第 5-9 行:关键字 [constructor] 表示类的构造函数。此类最多只能有一个构造函数。它用于创建并初始化类的实例。请注意,这里没有声明属性 [nom, prénom, âge];
- 第 11-36 行:类的属性。这里的内容与第 44 页“对象”第 4 段中已介绍的内容相同,仅语法有所不同;
- 第 41 行:创建一个类型为 [Personne] 的对象。 从现在起,对象 [personne] 可作为字面量对象使用。[typeof (personne)] 的值为“object”,且表达式 [personne instanceof (Personne)] 为真。因此,可以确定类实例的确切类型;
执行结果
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\classes\class-01.js"
personne= {"_nom":"Poirot","_prénom":"Hercule","_âge":66} object true
13.3. 脚本 [class-02]
该脚本展示了使用关键字 [extends] 继承类的可能性。
首先,我们将类 [Personne] 提取到模块 [Personne.js] 中:
// 类
class Personne {
// 构造函数
constructor(nom, prénom, âge) {
this.nom = nom;
this.prénom = prénom;
this.âge = âge;
}
// 获取器和设置器
get nom() {
return this._nom;
}
set nom(value) {
this._nom = value;
}
get prénom() {
return this._prénom;
}
set prénom(value) {
this._prénom = value;
}
get âge() {
return this._âge;
}
set âge(value) {
this._âge = value;
}
// toString 转换为 JSON
toString() {
return JSON.stringify(this);
}
}
// 导出类
export default Personne;
- 第 39 行:我们导出类 [Personne],以便脚本能够导入它;
脚本 [class-02] 创建了一个从类 [Personne] 派生的类 [Enseignant]:
// 导入
import Personne from './Personne';
// 类
class Enseignant extends Personne {
// 构造函数
constructor(nom, prénom, âge, discipline) {
super(nom, prénom, âge);
this.discipline = discipline;
}
// 获取器和设置器
get discipline() {
return this._discipline;
}
set discipline(value) {
this._discipline = value;
}
}
// 类调用
function main() {
const enseignant = new Enseignant("Poirot", "Hercule", 66, "détective");
console.log("enseignant=", enseignant.toString(), typeof (enseignant), enseignant instanceof Enseignant);
}
// 调用 main
main();
注释
- 第 2 行:从位于与 [class-02] 同一文件夹中的 [Personne.js] 模块导入类 [Personne];
- 第 5 行:类 [Enseignant] 通过关键字 [extends] 继承自类 [Personne]: 它为其添加了一个名为 [_discipline] 的属性,并附带相应的 getter 和 setter 方法;
- 第 8-11 行:类 [Enseignant] 的构造函数接收四个值,用于初始化该类的四个属性;
- 第 9 行:关键字 [super] 调用父类 [Personne] 的构造函数,从而初始化属性 [_nom, _prénom, _âge];
- 第 10 行:初始化 [Enseignant] 类所属的 [_discipline] 属性;
- 第 14-19 行:属性 [_discipline] 的 getter 和 setter;
- 第 25 行:创建一个 [Enseignant] 类型的对象;
- 第 26 行:调用方法 [enseignant.toString()]。类 [Enseignant] 没有此方法。 此时将自动调用其父类的该方法。该方法使得表达式 [JSON.stringify(this)](其中 [this] 将在这里表示对象 [Enseignant],而非对象 [Personne])。 这在面向对象编程中被称为类的多态性。对于非面向对象语言的 JavaScript 来说,这虽是个大词,但 JavaScript 在此处确实做到了我们期望的效果:它正确地显示了一位教师;
执行结果如下:
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\classes\class-02.js"
enseignant= {"_nom":"Poirot","_prénom":"Hercule","_âge":66,"_discipline":"détective"} object true
- 第 2 行:JavaScript 正确识别出变量 [enseignant] 的类型为 [Enseignant];
13.4. 脚本 [class-03]
脚本 [class-03] 表明子类可以重定义父类的属性和方法。在此,我们重定义了父类的 [toString] 方法:
// 导入
import Personne from './Personne';
// 类
class Enseignant extends Personne {
// 构造函数
constructor(nom, prénom, âge, discipline) {
super(nom, prénom, âge);
this.discipline = discipline;
}
// 获取器和设置器
get discipline() {
return this._discipline;
}
set discipline(value) {
this._discipline = value;
}
// 重写 toString
toString() {
return "[Enseignant]" + JSON.stringify(this);
}
}
// 调用类
function main() {
const enseignant = new Enseignant("Poirot", "Hercule", 66, "détective");
console.log("enseignant=", enseignant.toString(), typeof (enseignant), enseignant instanceof Enseignant);
}
// 调用 main
main();
执行结果如下:
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\classes\class-03.js"
enseignant= [Enseignant]{"_nom":"Poirot","_prénom":"Hercule","_âge":66,"_discipline":"détective"} object true
13.5. 脚本 [class-04]
脚本 [class-04] 再次展示了多态性的应用:当函数期望接收类型为 [Personne] 的形式参数时,我们可以传入 [Enseignant] 这样的派生类型。 事实上,由于派生关系,类型 [Enseignant] 拥有类型 [Personne] 的所有属性。
首先,我们将类型 [Enseignant] 提取到模块 [Enseignant.js] 中:
// 导入
import Personne from './Personne';
// 类
class Enseignant extends Personne {
// 构造函数
constructor(nom, prénom, âge, discipline) {
super(nom, prénom, âge);
this.discipline = discipline;
}
// 获取器和设置器
get discipline() {
return this._discipline;
}
set discipline(value) {
this._discipline = value;
}
}
// 导出类
export default Enseignant;
- 第 24 行:导出类 [Enseignant],以便其他脚本能够导入它;
脚本 [class-04] 如下:
// 导入
import Enseignant from './Enseignant';
import Personne from './Personne';
// 接受一个对象作为参数的函数
function show(personne) {
// 在所有情况下
console.log("paramètre=", personne.toString(), typeof (personne));
// Person 实例
if (personne instanceof Personne) {
console.log("personne=", personne.toString());
}
// 教师的实例
if (personne instanceof Enseignant) {
console.log("enseignant=", personne.toString());
}
}
// 调用 show 方法并传入一名教师
show(new Enseignant("Poirot", "Hercule", 66, "détective"));
show(new Personne("Marple", "Miss", 70));
- 第 6 行:函数 [show] 期望接收类型为 [Personne] 或其派生类型;
- 第 8 行:显示参数的字符串及其类型。我们将看到 [object];
- 第10-16行:可以判断参数类型是[Personne]还是[Enseignant]。因此,代码可以根据参数的实际类型进行调整;
执行结果如下:
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\classes\class-04.js"
paramètre= {"_nom":"Poirot","_prénom":"Hercule","_âge":66,"_discipline":"détective"} object
personne= {"_nom":"Poirot","_prénom":"Hercule","_âge":66,"_discipline":"détective"}
enseignant= {"_nom":"Poirot","_prénom":"Hercule","_âge":66,"_discipline":"détective"}
paramètre= {"_nom":"Marple","_prénom":"Miss","_âge":70} object
personne= {"_nom":"Marple","_prénom":"Miss","_âge":70}
- 第4行和第6行:JavaScript正确识别了类实例的类型;