Skip to content

13.

Image

在此我们介绍 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正确识别了类实例的类型;