Skip to content

13.

Image

在此我们将介绍 ECMAScript 6 类。首先,我们将展示函数其实已经可以作为类使用。

13.1. 脚本 [class-00]

以下脚本演示了函数的一种非常规用法。在此,它们被用作对象。


'use strict';
// a function can be used as an object
 
// an empty shell
function f() {
 
}
// to which external properties are attributed
f.prop1 = "val1";
f.show = function () {
  console.log(this.prop1);
}
// use of f
f.show();
 
// a function g operating as a class
function g() {
  this.prop2 = "val2";
  this.show = function () {
    console.log(this.prop2);
  }
}
// instantiate the function with [new]
new g().show();

注释

  • 第 5–7 行:函数 f 的主体未定义任何属性;
  • 第 9–12 行:属性是从外部赋值给函数 f 的;
  • 第 14 行:使用函数(对象)f。请注意,我们不写 [f()],而是直接写 [f]。这是对象的表示法;
  • 第 17–22 行:我们将函数 [g] 定义为一个具有属性和方法的类;
  • 第 24 行:通过 [new g()] 对函数 [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 引入了类(class)的概念,这使得我们现在无需使用函数即可创建类。

13.2. 脚本 [class-01]

[class-01] 脚本展示了一个 [Person] 类:


// class
class Personne {
 
  // manufacturer
  constructor(nom, prénom, âge) {
    this.nom = nom;
    this.prénom = prénom;
    this.âge = âge;
  }
 
  // getters and setters
  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 to JSON
  toString() {
    return JSON.stringify(this);
  }
}
 
// class call
function main() {
  const personne = new Personne("Poirot", "Hercule", 66);
  console.log("personne=", personne.toString(), typeof (personne), personne instanceof (Personne));
}
 
// hand call
main();

注释

  • 第 2 行:关键字 [class] 表示一个类;
  • 第 5–9 行:关键字 [constructor] 表示类构造函数。最多只能有一个。它用于构造和初始化类的实例。请注意,这里没有声明属性 [last_name, first_name, age]
  • 第 11–36 行:类属性。此处涉及的内容已在第 44 页的“对象”第 4 节中介绍过。仅语法有所不同;
  • 第 41 行:创建类型为 [Person] 的对象。从现在起,对象 [person] 将作为字面量对象使用。[typeof (person)] 的计算结果为“object”,且表达式 [person instanceof (Person)] 为真。因此,可以确定类实例的确切类型;

执行结果


[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] 关键字从类继承。

首先,我们将 [Person] 类移入一个名为 [Person.js] 的模块中:


// classe
class Personne {
 
  // constructeur
  constructor(nom, prénom, âge) {
    this.nom = nom;
    this.prénom = prénom;
    this.âge = âge;
  }
 
  // getters et setters
  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 en JSON
  toString() {
    return JSON.stringify(this);
  }
}
// export classe
export default Personne;
  • 第 39 行:我们导出 [Person] 类,以便脚本可以导入它;

[class-02] 脚本创建了一个从 [Person] 类派生的 [Teacher] 类:


// imports
import Personne from './Personne';
 
// classe
class Enseignant extends Personne {
 
  // constructeur
  constructor(nom, prénom, âge, discipline) {
    super(nom, prénom, âge);
    this.discipline = discipline;
  }
 
  // getters et setters
  get discipline() {
    return this._discipline;
  }
  set discipline(value) {
    this._discipline = value;
  }
 
}
 
// appel de la classe
function main() {
  const enseignant = new Enseignant("Poirot", "Hercule", 66, "détective");
  console.log("enseignant=", enseignant.toString(), typeof (enseignant), enseignant instanceof Enseignant);
}
 
// appel de main
main();

注释

  • 第 2 行:我们从 [Person.js] 模块导入 [Person] 类,该模块与 [class-02] 位于同一文件夹中;
  • 第 5 行:[Teacher] 类使用 [extends] 关键字继承自 [Person] 类:它添加了一个 [_subject] 属性,并附带相应的 getter 和 setter 方法;
  • 第 8–11 行:[Teacher] 类的构造函数接收四个值,用于初始化该类的四个属性;
  • 第 9 行:[super] 关键字调用父类 [Person] 的构造函数,从而初始化 [_last_name, _first_name, _age] 属性;
  • 第 10 行:初始化 [Teacher] 类的 [_discipline] 属性;
  • 第 14–19 行:[_discipline] 属性的 getter 和 setter 方法;
  • 第 25 行:创建了一个 [Teacher] 类型的对象;
  • 第 26 行:我们使用了 [teacher.toString()] 方法。[Teacher] 类本身并不包含此方法。 因此,系统会自动调用其父类的该方法。该方法渲染表达式 [JSON.stringify(this)],其中 [this] 在此处将是一个 [Teacher] 对象,而非 [Person] 对象。这在面向对象编程中被称为类多态性。对于 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 正确识别出变量 [teacher] 的类型为 [Teacher]

13.4. 脚本 [class-03]

[class-03] 脚本展示了子类可以重写父类的属性和方法。在此,我们重写了父类的 [toString] 方法:


// imports
import Personne from './Personne';
 
// classe
class Enseignant extends Personne {
 
  // constructeur
  constructor(nom, prénom, âge, discipline) {
    super(nom, prénom, âge);
    this.discipline = discipline;
  }
 
  // getters et setters
  get discipline() {
    return this._discipline;
  }
  set discipline(value) {
    this._discipline = value;
  }
 
  // redéfinition de toString
  toString() {
    return "[Enseignant]" + JSON.stringify(this);
  }
}
 
// appel de la classe
function main() {
  const enseignant = new Enseignant("Poirot", "Hercule", 66, "détective");
  console.log("enseignant=", enseignant.toString(), typeof (enseignant), enseignant instanceof Enseignant);
}
 
// appel de 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] 脚本再次展示了多态性的实际应用:当函数期望 [Person] 类型的形式参数时,我们可以传入 [Teacher] 这样的派生类型。事实上,由于派生关系,[Teacher] 类型拥有 [Person] 类型的所有属性。

首先,我们将 [Teacher] 类型提取到 [Teacher.js] 模块中:


// imports
import Personne from './Personne';
 
// classe
class Enseignant extends Personne {
 
  // constructeur
  constructor(nom, prénom, âge, discipline) {
    super(nom, prénom, âge);
    this.discipline = discipline;
  }
 
  // getters et setters
  get discipline() {
    return this._discipline;
  }
  set discipline(value) {
    this._discipline = value;
  }
 
}
 
// export classe
export default Enseignant;
  • 第 24 行:导出 [Teacher] 类,以便其他脚本可以导入它;

[class-04] 脚本如下:


// imports
import Enseignant from './Enseignant';
import Personne from './Personne';
 
// fonction acceptant une personne comme paramètre
function show(personne) {
  // dans tous les cas
  console.log("paramètre=", personne.toString(), typeof (personne));
  // instance de Personne
  if (personne instanceof Personne) {
    console.log("personne=", personne.toString());
  }
  // instance de Enseignant
  if (personne instanceof Enseignant) {
    console.log("enseignant=", personne.toString());
  }
}
 
// appel de show avec un enseignant
show(new Enseignant("Poirot", "Hercule", 66, "détective"));
show(new Personne("Marple", "Miss", 70));
  • 第 6 行:[show] 函数期望接收 [Person] 类型或其派生类型;
  • 第 8 行:显示参数的字符串及其类型。我们会发现 [object]
  • 第 10–16 行:我们可以判断它是 [Person] 类型还是 [Teacher] 类型。因此,代码可以根据参数的实际类型进行调整;

执行结果如下:


[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 正确识别了类实例的类型;