TypeScript中的private关键字和private字段之间有什么区别?
在TypeScript 3.8+中,使用TypeScript中的private关键字和private字段之间有什么区别?,typescript,class,private,encapsulation,class-fields,Typescript,Class,Private,Encapsulation,Class Fields,在TypeScript 3.8+中,使用private关键字标记成员private有什么区别: class PrivateKeywordClass { private value = 1; } 并使用#专用字段: 我应该选择一个而不是另一个吗?Private关键字 in-TypeScript是编译时注释。它告诉编译器属性只能在该类中访问: class PrivateKeywordClass { private value = 1; } const obj = new Priv
private
关键字标记成员private有什么区别:
class PrivateKeywordClass {
private value = 1;
}
并使用#
专用字段:
我应该选择一个而不是另一个吗?Private关键字
in-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{
构造函数(){
这个值=1;
}
}
从这里,您可以看到为什么private
关键字不提供任何运行时保护:在生成的JavaScript中,它只是一个普通的JavaScript属性
私人领域
确保属性在运行时保持私有:
class PrivateFieldClass{
#数值=1;
getValue(){返回此值。#value;}
}
const obj=新的PrivateFieldClass();
//您不能像这样在类外访问“#value”
obj.value==未定义//这不是您要查找的字段。
obj.getValue()==1//但是类本身可以访问私有字段!
//同时,在类之外使用私有字段是一个运行时语法错误:
目标值
//尝试访问另一个类的私有字段时
//运行时类型错误:
其他类{
#价值观;
getValue(obj){
返回obj.#value//TypeError:从不包含该字段的对象中读取私有字段#value
}
}
新的Other().getValue(新的PrivateKeywordClass());
如果尝试在类之外使用私有字段,TypeScript还将输出编译时错误:
私有字段来自于,也可以在普通JavaScript中使用
发出的JavaScript
如果您在TypeScript中使用私有字段,并针对较旧版本的JavaScript进行输出,例如es6
或es2018
,TypeScript将尝试生成模拟私有字段运行时行为的代码
class PrivateFieldClass{
构造函数(){
_x、 设置(本,1);
}
}
_x=新的WeakMap();
如果您的目标是esnext
,则TypeScript将发出专用字段:
class PrivateFieldClass{
构造函数(){
这个#x=1;
}
#x;
}
我应该用哪一个?
这取决于你想要达到的目标
private
关键字是一个很好的默认值。它完成了设计的目标,多年来一直被TypeScript开发人员成功使用。如果您有一个现有的代码库,则不需要将所有代码切换为使用私有字段。如果您不是针对esnext
,这一点尤其正确,因为TS为私有字段发出的JS可能会影响性能。还要记住,私有字段与private
关键字还有其他细微但重要的区别
但是,如果您需要强制运行时私有性或输出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 }
- 没有初始值设定项的
关键字private property将不会在发出的JavaScript中生成属性声明:private
汇编至:class PrivateKeywordClass { private value?: string; getValue() { return this.value; } }
而私有字段始终生成属性声明:class PrivateKeywordClass{ getValue(){返回this.value;} }
编译到(当目标为esnext时):class PrivateKeywordClass { #value?: string; getValue() { return this.#value; } }
class PrivateKeywordClass{ #价值观; getValue(){返回此值。#value;} }
#
-私有字段
前言:
- 同义词:私有、硬私有、运行时私有
#
-私有字段提供编译时和运行时隐私,这是不可“黑客”的。它是一种防止从类主体外部访问成员的机制
安全类继承
#
-私有字段具有唯一的作用域。可以实现类层次结构,而不会意外覆盖具有相同名称的私有属性
class A {
#a = "a";
fnA() { return this.#a; }
}
class B extends A {
#a = "b";
fnB() { return this.#a; }
}
const b = new B();
b.fnA(); // returns "a" ; unique property #a in A is still retained
b.fnB(); // returns "b"
幸运的是,当private
属性有被覆盖的危险时,TS编译器会发出一个错误(请参阅)。但是由于编译时特性的性质,在运行时一切都是可能的,因为编译错误会被忽略和/或使用发出的JS代码
外部图书馆
库作者可以重构
-私有标识符,而不会对客户端造成破坏性的更改。另一端的库用户受到保护,无法访问内部字段
JSAPI省略了#
-私有字段
内置JS函数和方法忽略#
-私有字段。这会导致在运行时选择更可预测的属性。示例:Object.keys
,Object.entries
,JSON.stringify
,for..in
循环和其他
class A {
#a: number;
constructor(a: number) {
this.#a = a;
}
}
let foo: A = new A(42);
foo.#a; // error, not allowed outside class bodies
(foo as any).#bar; // still nope.
class A {
#a = "a";
fnA() { return this.#a; }
}
class B extends A {
#a = "b";
fnB() { return this.#a; }
}
const b = new B();
b.fnA(); // returns "a" ; unique property #a in A is still retained
b.fnB(); // returns "b"
class Foo {
#bar = 42;
baz = "huhu";
}
Object.keys(new Foo()); // [ "baz" ]
class A {
constructor(private a: number) { }
}
const a = new A(10);
a.a; // TS compile error
(a as any).a; // works
const casted: any = a; casted.a // works
class C {
private foo = 10;
}
const res = new C()["foo"]; // 10, res has type number