问题描述
在TypeScript 3.8+中,使用 private
关键字将成员标记为私有之间有什么区别:
class PrivateKeywordClass {
私有值= 1;
}
并使用#
专用字段
私有字段来自,也可以在普通JavaScript中使用。
发出的JavaScript
如果您在TypeScript中使用私有字段,并且将JavaScript的较旧版本定位为目标,例如 es6
或 es2018
,TypeScript将尝试生成模拟私有字段运行时行为的代码
class PrivateFieldClass {
Constructor(){
_x.set(this,1) ;
}
}
_x = new WeakMap();
如果您要定位 esnext
,则TypeScript将发出私有字段:
class PrivateFieldClass {
Constructor(){
this。#x = 1;
}
#x;
}
我应该使用哪个?
这取决于您要实现的目标。
private
关键字是很好的默认设置。它完成了其设计要完成的任务,并已被TypeScript开发人员成功使用多年。而且,如果您已有代码库,则无需切换所有代码即可使用私有字段。如果您不针对 esnext
,则尤其如此,因为TS为私有字段发出的JS可能会对性能产生影响。另外请记住,私有字段与 private
关键字
还有其他细微但重要的区别,但是如果您需要强制运行时的私有性,或者输出 esnext
JavaScript,而不是使用私有字段。
还请记住,随着私有字段在JavaScript / TypeScript生态系统中的普及,组织/社区关于使用另一种约定的约定也将不断发展。
其他音符差异
-
Object.getOwnPropertyNames 和类似方法
-
私有字段未通过
JSON.stringify $序列化c $ c>
-
在继承方面有重要的优势案例。
例如,TypeScript禁止声明
class基类{
private值= 1;
}
类别Sub扩展Base {
私人价值= 2; //编译错误:
}
对于私有字段,情况并非如此:
class Base {
#value = 1;
}
class Sub扩展Base {
#value = 2; //没有错误
}
-
A
private
没有初始化程序的关键字私有属性不会在发出的JavaScript中生成属性声明:class PrivateKeywordClass {
私有值?:字符串;
getValue(){返回this.value; }
}
编译为:
class PrivateKeywordClass {
getValue(){返回this.value; }
}
而私有字段始终会生成属性声明:
class PrivateKeywordClass {
#value ?:字符串;
getValue(){返回this。#value; }
}
编译为(当定位
esnext $时) c $ c>):
class PrivateKeywordClass {
#value;
getValue(){返回this。#value; }
}
进一步阅读:
In TypeScript 3.8+, what are the differences between using the private
keyword to mark a member private:
class PrivateKeywordClass {
private value = 1;
}
And using the #
private fields proposed for JavaScript:
class PrivateFieldClass {
#value = 1;
}
Should I prefer one over the other?
Private keyword
The private keyword in TypeScript is a compile time annotation. It tells the compiler that a property should only be accessible inside that class:
class PrivateKeywordClass {
private value = 1;
}
const obj = new PrivateKeywordClass();
obj.value // compiler error: Property 'value' is private and only accessible within class 'PrivateKeywordClass'.
However compile time checking can be easily bypassed, for example by casting away the type information:
const obj = new PrivateKeywordClass();
(obj as any).value // no compile error
The private
keyword is also not enforced at runtime
Emitted JavaScript
When compiling TypeScript to JavaScript, the private
keyword is simply removed:
class PrivateKeywordClass {
private value = 1;
}
Becomes:
class PrivateKeywordClass {
constructor() {
this.value = 1;
}
}
From this, you can see why the private
keyword does not offer any runtime protection: in the generated JavaScript it's just a normal JavaScript property.
Private fields
Private fields ensure that properties are kept private 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());
TypeScript will also output a compile time error if you try using a private field outside of a class:
Private fields come from a JavaScript proposal and also work in normal JavaScript.
Emitted JavaScript
If you use private fields in TypeScript and are targeting older versions of JavaScript for your output, such as es6
or es2018
, TypeScript will try to generate code that emulates the runtime behavior of private fields
class PrivateFieldClass {
constructor() {
_x.set(this, 1);
}
}
_x = new WeakMap();
If you are targeting esnext
, TypeScript will emit the private field:
class PrivateFieldClass {
constructor() {
this.#x = 1;
}
#x;
}
Which one should I use?
It depends on what you are trying to achieve.
The private
keyword is a fine default. It accomplishes what it was designed to accomplish and has been used successfully by TypeScript developers for years. And if you have an existing codebase, you do not need to switch all of your code to use private fields. This is especially true if you are not targeting esnext
, as the JS that TS emits for private fields may have a performance impact. Also keep in mind that private fields have other subtle but important differences from the private
keyword
However if you need to enforce runtime privateness or are outputting esnext
JavaScript, than you should use private fields.
Also keep in mind that organization/community conventions on using one or the other will also evolve as private fields become more widespread within the JavaScript/TypeScript ecosystems
Other differences of note
Private fields are not returned by
Object.getOwnPropertyNames
and similar methodsPrivate fields are not serialized by
JSON.stringify
There are importance edge cases around inheritance.
TypeScript for example forbids declaring a private property in a subclass with the same name as a private property in the superclass.
class Base { private value = 1; } class Sub extends Base { private value = 2; // Compile error: }
This is not true with private fields:
class Base { #value = 1; } class Sub extends Base { #value = 2; // Not an error }
A
private
keyword private property without an initializer will not generate a property declaration in the emitted JavaScript:class PrivateKeywordClass { private value?: string; getValue() { return this.value; } }
Compiles to:
class PrivateKeywordClass { getValue() { return this.value; } }
Whereas private fields always generate a property declaration:
class PrivateKeywordClass { #value?: string; getValue() { return this.#value; } }
Compiles to (when targetting
esnext
):class PrivateKeywordClass { #value; getValue() { return this.#value; } }
Further reading:
这篇关于TypeScript中的private关键字和private字段有什么区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!