typescript - TypeScript 中的 private 关键字和 private 字段有什么区别?

标签 typescript class private encapsulation class-fields

在 TypeScript 3.8+ 中,使用 private 有什么区别?将成员标记为私有(private)的关键字:

class PrivateKeywordClass {
    private value = 1;
}

并使用 #私有(private)领域 proposed for JavaScript :
class PrivateFieldClass {
    #value = 1;
}

我应该更喜欢一个吗?

最佳答案

私有(private)关键字

private keyword在 TypeScript 中是 编译时间注解。它告诉编译器一个属性只能在该类中访问:

class PrivateKeywordClass {
    private value = 1;
}

const obj = new PrivateKeywordClass();
obj.value // compiler error: Property 'value' is private and only accessible within class 'PrivateKeywordClass'.

但是编译时检查可以很容易地绕过,例如通过丢弃类型信息:
const obj = new PrivateKeywordClass();
(obj as any).value // no compile error
private关键字也不会在运行时强制执行

发出的 JavaScript

将 TypeScript 编译为 JavaScript 时,private关键字被简单地删除:
class PrivateKeywordClass {
    private value = 1;
}

变成:

class PrivateKeywordClass {
    constructor() {
        this.value = 1;
    }
}

从这里,你可以看到为什么 private关键字不提供任何运行时保护:在生成的 JavaScript 中,它只是一个普通的 JavaScript 属性。

私有(private)字段

Private fields确保属性保密在运行时 :

class PrivateFieldClass {
    #value = 1;

    getValue() { return this.#value; }
}

const obj = new PrivateFieldClass();

// You can't access '#value' outside of class like this
obj.value === undefined // This is not the field you are looking for.
obj.getValue() === 1 // But the class itself can access the private field!

// Meanwhile, using a private field outside a class is a runtime syntax error:
obj.#value

// While trying to access the private fields of another class is 
// a runtime type error:
class Other {
    #value;

    getValue(obj) {
        return obj.#value // TypeError: Read of private field #value from an object which did not contain the field
    }
}

new Other().getValue(new PrivateKeywordClass());

如果您尝试在类之外使用私有(private)字段,TypeScript 也会输出编译时错误:

Error on accessing a private field

私有(private)字段来自 JavaScript proposal并且也可以在普通的 JavaScript 中工作。

发出的 JavaScript

如果您在 TypeScript 中使用私有(private)字段,并且输出的目标是旧版本的 JavaScript,例如 es6es2018 ,TypeScript 将尝试生成模拟私有(private)字段的运行时行为的代码

class PrivateFieldClass {
    constructor() {
        _x.set(this, 1);
    }
}
_x = new WeakMap();

如果您的目标是 esnext , TypeScript 将发出私有(private)字段:

class PrivateFieldClass {
    constructor() {
        this.#x = 1;
    }
    #x;
}

我应该使用哪一个?

这取决于您要达到的目标。
private关键字是一个很好的默认值。它完成了它的设计目标,并且多年来一直被 TypeScript 开发人员成功使用。如果您有现有的代码库,则无需切换所有代码以使用私有(private)字段。如果您的目标不是 esnext,则尤其如此。 ,因为 TS 为私有(private)字段发出的 JS 可能会对性能产生影响。还要记住,私有(private)字段与 private 有其他细微但重要的区别。关键词

但是,如果您需要强制执行运行时隐私或输出 esnext JavaScript,而不是您应该使用私有(private)字段。

还要记住,随着私有(private)字段在 JavaScript/TypeScript 生态系统中变得越来越普遍,使用其中一种或另一种的组织/社区约定也会发展

其他注意事项
  • Object.getOwnPropertyNames 不返回私有(private)字段和类似的方法
  • JSON.stringify 不序列化私有(private)字段
  • 继承存在一些重要的边缘案例。

    例如,TypeScript 禁止在子类中声明与父类(super class)中的私有(private)属性同名的私有(private)属性。
    class Base {
        private value = 1;
    }
    
    class Sub extends Base {
        private value = 2; // Compile error:
    }
    

    这不适用于私有(private)字段:
    class Base {
        #value = 1;
    }
    
    class Sub extends Base {
        #value = 2; // Not an error
    }
    
  • 一个 private没有初始化器的关键字私有(private)属性将不会在发出的 JavaScript 中生成属性声明:
    class PrivateKeywordClass {
        private value?: string;
        getValue() { return this.value; }
    }
    

    编译为:

    class PrivateKeywordClass {
        getValue() { return this.value; }
    }
    

    而私有(private)字段总是生成一个属性声明:
    class PrivateKeywordClass {
        #value?: string;
        getValue() { return this.#value; }
    }
    

    编译为(当目标为 esnext 时):

    class PrivateKeywordClass {
        #value;
        getValue() { return this.#value; }
    }
    

  • 进一步阅读:
  • The future of the "private" keyword
  • TypeScript PR that added private fields
  • 关于typescript - TypeScript 中的 private 关键字和 private 字段有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59641564/

    相关文章:

    在共享点页面上加载 SPFX Webpart 之前调用 Javascript

    javascript - WebGL 帧缓冲区为空白

    c++ - 为什么这里不能访问enum?

    C++ 子类构造函数

    java - 继承java中 protected 成员与私有(private)成员

    .net - 使用 .NET 反射的私有(private)方法。为什么?

    javascript - 如何将 javascript 代码转换为 Angular

    angular - 如何将 popper 与 Angular 一起使用

    Java泛型方法

    javascript - 使用私有(private)变量创建单例的方法?