私人关键字
The 私有关键字 https://www.typescriptlang.org/docs/handbook/classes.html#understanding-private在 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
The private
关键字在运行时也不会强制执行
发出的 JavaScript
将 TypeScript 编译为 JavaScript 时,private
关键字被简单地删除:
class PrivateKeywordClass {
private value = 1;
}
Becomes:
class PrivateKeywordClass {
constructor() {
this.value = 1;
}
}
从这里你可以看出为什么private
关键字不提供任何运行时保护:在生成的 JavaScript 中,它只是一个普通的 JavaScript 属性。
私人领域
私人领域 https://github.com/tc39/proposal-class-fields#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());
如果您尝试在类之外使用私有字段,TypeScript 也会输出编译时错误:
私有字段源自TC-39 ECMAScript 提案 https://github.com/tc39/proposal-class-fields#private-fields并且是 2021 ECMAScript 规范的一部分,这意味着它们可以在普通 JavaScript 和 TypeScript 中使用。
发出的 JavaScript
如果您在 TypeScript 中使用私有字段,并且目标是 ES2021 或更早版本的 JavaScript 进行输出,TypeScript 将生成模拟私有字段运行时行为的代码,使用WeakMap https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap (source https://www.typescriptlang.org/docs/handbook/2/classes.html#caveats)
class PrivateFieldClass {
constructor() {
_x.set(this, 1);
}
}
_x = new WeakMap();
如果您的目标是 ES2021 之后的任何内容,TypeScript 将发出私有字段:
class PrivateFieldClass {
constructor() {
this.#x = 1;
}
#x;
}
我应该使用哪一个?
这取决于您想要实现的目标。
The private
关键字是一个很好的默认值。它实现了它的设计目标,并且已被 TypeScript 开发人员成功使用多年。如果您有现有的代码库,则无需将所有代码切换为使用私有字段。如果您没有瞄准目标,则尤其如此esnext
,因为 TS 为私有字段发出的 JS 可能会对性能产生影响。另请记住,私有字段与私有字段还有其他微妙但重要的区别private
keyword
但是,如果您需要强制运行时隐私或正在输出esnext
JavaScript,而不是你应该使用私有字段。
另请记住,随着私有字段在 JavaScript/TypeScript 生态系统中变得更加普遍,组织/社区关于使用其中一种或另一种的约定也将不断发展
其他值得注意的差异
-
私有字段不会被返回Object.getOwnPropertyNames
和类似的方法
-
私有字段不被序列化JSON.stringify
-
继承有一些重要的边缘情况。
例如,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
}
-
A private
没有初始化器的关键字 private property 将不会在发出的 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; }
}
进一步阅读:
- “private”关键字的未来 https://github.com/microsoft/TypeScript/issues/31670
- 添加私有字段的 TypeScript PR https://github.com/microsoft/TypeScript/pull/30829