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

class PrivateKeywordClass {
    private value = 1;
}

使用#个私有字段proposed for JavaScript:

class PrivateFieldClass {
    #value = 1;
}

我应该更喜欢其中一个吗?

推荐答案

Private keyword

TypeScript中的private keywordcompile time注释.它告诉编译器,属性只能在该类中访问:

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

privatekeyword也不会在运行时强制执行

发出的JavaScript

将TypeScript编译为JavaScript时,只需删除private关键字:

class PrivateKeywordClass {
    private value = 1;
}

变成:

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

由此可以看出为什么private关键字不提供任何运行时保护:在生成的JavaScript中,它只是一个普通的JavaScript属性.

Private fields

Private fields确保财产私有at runtime:

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());

如果try 在类之外使用私有字段,TypeScript还会输出编译时错误:

Error on accessing a private field

私有字段来自JavaScript proposal,也适用于普通JavaScript.

发出的JavaScript

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

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

如果目标是esnext,TypeScript将发出私有字段:

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

Which one should I use?

这取决于你想要实现什么.

private关键字是一个很好的默认值.它完成了设计的目标,多年来一直被TypeScript开发人员成功使用.如果您有一个现有的代码库,则不需要将所有代码切换为使用私有字段.如果不是针对esnext,这一点尤其正确,因为TS为私有字段发出的JS可能会影响性能.还要记住,私有字段与private关键字还有其他细微但重要的区别

然而,如果您需要强制运行时私有性,或者要输出esnext个JavaScript,那么应该使用私有字段.

还请记住,随着私有字段在JavaScript/TypeScript生态系统中变得越来越广泛,组织/社区关于使用其中一个的约定也会发生变化

Other differences of note

  • 私有字段不是由Object.getOwnPropertyNames或类似的方法返回的

  • 私有字段不按JSON.stringify序列化

  • 关于继承,有一些重要的边缘 case .

    例如,TypeScript禁止在子类中声明与超类中私有属性同名的私有属性.

    class Base {
        private value = 1;
    }
    
    class Sub extends Base {
        private value = 2; // Compile error:
    }
    

    对于私有字段,情况并非如此:

    class Base {
        #value = 1;
    }
    
    class Sub extends Base {
        #value = 2; // Not an error
    }
    
  • 没有初始值设定项的private关键字私有属性不会在发出的JavaScript中生成属性声明:

    class PrivateKeywordClass {
        private value?: string;
        getValue() { return this.value; }
    }
    

    汇编至:

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

    而私有字段总是生成属性声明:

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

    编译为(目标为esnext时):

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

进一步阅读:

Typescript相关问答推荐

JS Redux Devtools扩展不工作

如果一个变量不是never类型,我如何创建编译器错误?

如何将类型从变量参数转换为返回中的不同形状?

如何推断计算逻辑的类型

Next.js错误:不变:页面未生成

PrimeNG日历需要找到覆盖默认Enter键行为的方法

类型脚本返回的类型包含无意义的泛型类型名称

如何在Reaction Query Builder中添加其他字段?

如果数组使用Reaction-Hook-Form更改,则重新呈现表单

在列表的指令中使用intersectionObservable隐藏按钮

在打印脚本中使用泛型扩展抽象类

->;Boolean类型的键不能分配给类型Never

如何从一个泛型类型推断多个类型

在Thunk中间件中输入额外参数时Redux工具包的打字脚本错误

类型脚本中参数为`T`和`t|unfined`的重载函数

确保财产存在

如何限定索引值必须与相应属性的id字段相同

Angular 16:无法覆盖;baseUrl;在tsconfig.app.json中

TypeScript是否不知道其他函数内部的类型判断?

如何按成员资格排除或 Select 元组?