Skip to content

Files

Latest commit

2d68a5d · Jan 8, 2022

History

History
240 lines (179 loc) · 6.55 KB

09.md

File metadata and controls

240 lines (179 loc) · 6.55 KB

九、类

JavaScript 中的类提供了一种重用代码的好方法。到目前为止,还没有一种更简单的方法来干净地支持类和继承。我们现在来看看这两个。

考虑代码清单 127.js 文件中的以下Animal类:

代码清单 127

  export class Animal {
    constructor(name) {
      this.name = name;
    }
    greeting(sound) {
      return `A ${this.name}
  ${sound}`;
    }
    static echo(msg) {
      console.log(msg);
    }  
  }

就像函数一样,我们可以导出要加载到另一个模块中的类。我们宣布Animal级。我们有一个构造函数,它接受一个名为name的参数。最后,我们有一个名为greeting的函数,它接受一个名为sound的参数。greeting函数在调用时使用新的字符串插值语法来创建自定义消息。

我相信你已经注意到了静态功能echostatic关键字允许我们将功能标记为静态。这允许我们通过类名而不是实例来引用函数。

让我们看看代码清单 128.js 文件中的AnimalClient类:

代码清单 128

  import {Animal} from
  './code-listing-127';

  export class AnimalClient {
    constructor() {
      this.animal = new
  Animal("Dog");
      console.log(this.animal.greeting("barks"));
    }
  }

  let ac = new
  AnimalClient();
  Animal.echo("roof,
  roof");

正如我们之前看到的,我们正在从代码清单 126.js 文件中导入animal类。接下来,我们构建一个AnimalClient类,该类在其构造函数中创建一个新的Animal实例,并将greeting函数呈现给控制台。因为我们正在测试这段代码,所以我们在课后有一行代码来启动一切并让事情进行下去,还有一个对Animal类上的static函数echo的调用。即使我们使用同一个文件中的AnimalClient,我们也要export这个类,以便从另一个文件中访问它。

以下是前面代码的输出:

代码清单 129

  A Dog barks
  roof, roof

现在我们已经了解了类的语法,让我们扩展Animal类,并创建另一个继承自它的类。考虑代码清单 129.js 文件中的以下代码:

代码清单 130

  export class Animal {
    constructor(name) {
      this.name = name;
    }
    greeting(sound) {
      return `A ${this.name}
  ${sound}`;
    }
    static echo(input) {
      console.log(input);
    }
  }
  export class Canine extends
  Animal {
    constructor() {
      super("canine");
    }
    static echo() {
      super.echo("bow wow");
    }
  }

Animal类没有变化,但是我们在文件中添加了另一个类,叫做Canine。如你所料,这个班extends这个Animal班。这将使用super关键字将字符串canine传递给基础构造函数。我们也可以使用super 来访问基类之外的函数和属性。这在echo函数调用基础实现并传递字符串bow wow的例子中有所说明。

现在让我们看看吸气剂和沉降剂。考虑代码清单 131.js 文件中的Animal类:

代码清单 131

  export class Animal {
    constructor(name) {
      this.name = name;
      this.color
  = "brown";
    }
    get color() { 
      return this._color;
    }
    set color(value) {
      this._color
  = value;
    }
    toString() {
      return console.log(`I am a ${this.name}
  I am ${this.color} in color.`);
    }
    static echo(input) {
      console.log(input);
    }
  }

如你所见,我们使用了两个新的关键词:getset。这允许我们通过包装其他变量或执行其他操作来提供获取器和设置器。这可能非常强大,允许您集中对变量的所有访问。

让我们看看这是如何在代码清单 132.js 文件中的AnimalClient类中使用的:

代码清单 132

  import {Animal} from
  './code-listing-131';

  export class AnimalClient {
    constructor() {
      this.animal = new
  Animal("dog");
      this.animal.toString();
    }
  }

  let ac = new
  AnimalClient();

最后,下面是前面代码示例的输出:

代码清单 133

  I am a dog.  I am brown in color.

在 ES6 之前,内建的子类化是极其困难的。考虑以下不使用内置的示例:

代码清单 134

  function
  SuperCtor(arg1) {

  ...
  }
  function
  SubCtor(arg1, arg2) { 

  SuperCtor.call(this, arg1);
  }
  SubCtor.prototype = Object.create(SuperCtor.prototype);

这是子类化另一个对象的典型方法。然而,当我们处理像数组、日期和 DOM 元素这样的内置元素时,这几乎是不可能的。在 JavaScript 中,如果调用内置构造函数,每个函数内部都会发生两个步骤:

  1. 分配–创建一个实例inst,一个原型为C.prototype的对象(如果该值不是对象,则使用Object.prototype)。
  2. 初始化–通过C.call(inst, arg1, arg2, …) .初始化inst如果调用的结果是一个对象,则返回它。否则,返回inst

如果我们使用前面显示的同一个子类模式,它根本不会工作。然而,有了 ES6,我们现在有能力将内置的子类化。

名为Ctor的函数的对象构造现在使用两个阶段,这两个阶段都是虚拟分派的:

  • 调用Ctor [@@create]分配对象,安装任何特殊行为。
  • 在新实例上调用constructor进行初始化。

已知的@@create符号可通过Symbol.create获得。内置现在暴露@@create明确。

考虑下面的代码示例:

代码清单 135

  // Pseudo-code of
  Array
  class Array {
    constructor(...args) { /* ... */ }
    static
  [Symbol.create]() {
        //
  Install special [[DefineOwnProperty]]
        //
  to magically update 'length'
    }
  }

  // User code of
  Array subclass
  class MyArray extends Array
  {
    constructor(...args) { super(...args); }
  }
  // Two-phase
  'new':
  // 1) Call
  @@create to allocate object
  // 2) Invoke
  constructor on new instance
  var arr = new
  MyArray();
  arr[1]
  = 12;
  console.log(arr.length == 2);
  console.log(arr);

第一个类是原生 JavaScript Array类的一个例子。这里重要的是理解通过使用@@create符号我们可以扩展内置类。创建您自己的Array所需的代码是微不足道的,尽管它没有做任何不同的事情。

以下是前面代码的输出:

代码清单 136

  true
  [ , 12 ]

输出中的逗号通过返回在布尔表达式中计算长度的结果来指示长度确实是 2。它还向控制台呈现数组,我们可以很容易地看到第一个索引位置没有赋值。