十甫寸木南
Articles10
Tags6
Categories0
TS Private Fields

TS Private Fields

文章 PDF 版

官网说明

Handbook - TypeScript 3.8

概述

私有字段的支持是从 TypeScript 3.8 开始的。(私有字段目前在 EcmaScript 中尚处于 stage-3 )

私有字段以 # 开始,如下:

class Person {
    #name: string

    constructor(name: string) {
        this.#name = name;
    }

    greet() {
        console.log(`Hello, my name is ${this.#name}!`);
    }
}

let jeremy = new Person("Jeremy Bearimy");

jeremy.#name
//     ~~~~~
// Property '#name' is not accessible outside class 'Person'
// because it has a private identifier.

私有字段的用法

私有字段的使用规则:

  • 私有字段以 # 开始。
  • 每个私有字段的名字,在被包含的类中,都是唯一的。
  • TypeScript 辅助功能修饰符(public、private 等)不能在私有字段上使用。
  • 私有字段不能在所包含的类之外访问;即使是对于 JavaScript 使用者来说也是如此。

针对以上规则的第 2 条 “每个私有字段的名字,在被包含的类中,都是唯一的。”,有以下示例对其做出解释:

  • 常规属性声明容易在子类中被改写,私有字段不会被子类改写,每个私有字段,在所包含的类中,都是唯一的。

      // 常规属性声明
      class C {
        foo = 10;
    
        cHelper() {
          return this.foo;
        }
      }
    
      class D extends C {
        foo = 20;
    
        dHelper() {
          return this.foo;
        }
      }
    
      let instance = new D();
      // 'this.foo' refers to the same property on each instance.
      console.log(instance.cHelper()); // prints '20'
      console.log(instance.dHelper()); // prints '20'
      // 私有字段声明
      class C {
          #foo = 10;
    
          cHelper() {
              return this.#foo;
          }
      }
    
      class D extends C {
          #foo = 20;
    
          dHelper() {
              return this.#foo;
          }
      }
    
      let instance = new D();
      // 'this.#foo' refers to a different field within each class.
      console.log(instance.cHelper()); // prints '10'
      console.log(instance.dHelper()); // prints '20'

对于私有字段的使用,还需要注意的一点是:除非在某个对象的类型声明中声明了某个私有字段,否则,在未声明该私有字段的对象中无法使用该私有字段,会报错。

class Square {
    #sideLength: number;

    constructor(sideLength: number) {
        this.#sideLength = sideLength;
    }

    equals(other: any) {
        return this.#sideLength === other.#sideLength;
    }
}

const a = new Square(100);
const b = { sideLength: 100 };

// Boom!
// TypeError: attempted to get private field on non-instance
// This fails because 'b' is not an instance of 'Square'.
console.log(a.equals(b));

私有字段的尝试

在看过对私有字段的概述及用法介绍后,我实际尝试了一番,但却碰到了一些问题,具体如下:

  • 示例代码:

      // private-field.ts
      class Person {
          #name: string;
    
          constructor(name: string) {
              this.#name = name;
          }
    
          greet() {
              console.log(`Hello, my name is ${this.#name}!`);
          }
      }
    
      const jeremy = new Person("Jeremy Bearimy");
    
      jeremy.greet();

    以上示例代码完全是从官网 Copy 下来的,但是在 IDE 中却显示出错!

    Private%20Fields/Untitled.png

    这是怎么回事呢?

  • 看一下报错信息:

      // TS18028: Private identifiers are only available when targeting ECMAScript 2015 and higher.
      // Private 标识符仅在 ECMAScript 2015 及更高版本可用

    原来如此!我当前的版本不支持私有字段!

    但是不对啊,我的 TypeScript 版本就是 3.8 啊!

    Private%20Fields/Untitled%201.png

    那是怎么回事呢?在 stackoverflow 的一个提问中我找到了原因:我的 tsconfig.json 配置错了

    Private%20Fields/Untitled%202.png

    我的编译目标版本错了,是 es5 ,而报错信息已经明确提示了,要求的版本是 ES 2015 及以上

  • 解决方案

    找到了问题所在,那么,解决起来就很简单了!

    Private%20Fields/Untitled%203.png

    可以看到,在将编译目标版本改到 es2015 后,原本 private-field.ts 上的出错提示已经消失了。

  • 运行效果

    Private%20Fields/Untitled%204.png

  • 私有字段的效果

    示例已经顺利运行起来了,那么,到底这个私有字段有没有用呢?

    在之前的示例代码中加上一行:

      class Person {
          #name: string;
    
          constructor(name: string) {
              this.#name = name;
          }
    
          greet() {
              console.log(`Hello, my name is ${this.#name}!`);
          }
      }
    
      const jeremy = new Person("Jeremy Bearimy");
    
      jeremy.greet();  
      console.log(jeremy.#name);
      //                 ~~~~~
      // TS18013: Property '#name' is not accessible outside class 'Person' because it has a private identifier.

    报错了!

    Private%20Fields/Untitled%205.png

    果然,私有字段还是非常犀利的!😊😊😊

私有字段和 private 修饰符的区别

  • TypeScript 中的 private 修饰符在编译为 JavaScript 后就完全被删掉了,相当于实际上是没有用的。
  • 私有字段即便编译为 JavaScript 也会存在。

私有字段的支持

由于目前 ECMAScript 对私有字段的支持尚在 stage-3 ,虽然可以用 Babel 实现在 JavaScript 中使用私有字段,但是不使用插件的话,将 TypeScript 编译为 JavaScript 会报错。

当然,也有可能是可以编译成功的,但是我目前由于没有深入地研究,所以暂时还没有实验成功。

Private%20Fields/Untitled%206.png