简体   繁体   English

TypeScript 中的私有关键字和私有字段有什么区别?

[英]What are the differences between the private keyword and private fields in TypeScript?

In TypeScript 3.8+, what are the differences between using the private keyword to mark a member private:在 TypeScript 3.8+ 中,使用private关键字将成员标记为私有有什么区别:

class PrivateKeywordClass {
    private value = 1;
}

And using the # private fields proposed for JavaScript :并使用#私人领域提出的JavaScript

class PrivateFieldClass {
    #value = 1;
}

Should I prefer one over the other?我应该更喜欢一个吗?

Private keyword私有关键字

The private keyword in TypeScript is a compile time annotation. TypeScript 中的private 关键字是一个编译时注释。 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 private关键字也不会在运行时强制执行

Emitted JavaScript发出的 JavaScript

When compiling TypeScript to JavaScript, the private keyword is simply removed:将 TypeScript 编译为 JavaScript 时,只需删除private关键字:

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关键字不提供任何运行时保护:在生成的 JavaScript 中,它只是一个普通的 JavaScript 属性。

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:如果您尝试在类之外使用私有字段,TypeScript 也会输出编译时错误:

访问私有字段时出错

Private fields come from a JavaScript proposal and also work in normal JavaScript.私有字段来自JavaScript 提案,也适用于普通 JavaScript。

Emitted JavaScript发出的 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如果您在 TypeScript 中使用私有字段并针对旧版本的 JavaScript 输出,例如es6es2018 ,TypeScript 将尝试生成模拟私有字段运行时行为的代码

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

If you are targeting esnext , TypeScript will emit the private field:如果您的目标是esnext ,TypeScript 将发出私有字段:

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. private关键字是一个很好的默认值。 It accomplishes what it was designed to accomplish and has been used successfully by TypeScript developers for years.它完成了它的设计目标,并已被 TypeScript 开发人员成功使用多年。 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.如果您的目标不是esnext ,则尤其如此,因为 TS 为私有字段发出的 JS 可能会对性能产生影响。 Also keep in mind that private fields have other subtle but important differences from the private keyword还要记住,私有字段与private关键字有其他微妙但重要的区别

However if you need to enforce runtime privateness or are outputting esnext JavaScript, than you should use private fields.但是,如果您需要强制执行运行时隐私或输出esnext JavaScript,那么您应该使用私有字段。

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另请记住,随着私有字段在 JavaScript/TypeScript 生态系统中变得越来越普遍,使用其中一种或另一种的组织/社区约定也将发展

Other differences of note其他注意事项

  • Private fields are not returned by Object.getOwnPropertyNames and similar methods Object.getOwnPropertyNames和类似方法不返回私有字段

  • Private fields are not serialized by JSON.stringify私有字段不会被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.例如,TypeScript 禁止在子类中声明与超类中的私有属性同名的私有属性。

     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:没有初始化器的private关键字私有属性不会在发出的 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 ):编译为(针对esnext ):

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

Further reading:进一步阅读:

Use cases: # -private fields用例: # -private 字段

Preface:前言:

Compile-time and run-time privacy编译时运行时隐私

# -private fields provide compile-time and run-time privacy, which is not "hackable". # -private 字段提供编译时运行时隐私,这是不可“破解的”。 It is a mechanism to prevent access to a member from outside the class body in any direct way .它是一种防止以任何直接方式从类主体外部访问成员的机制。

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.

Safe class inheritance安全的类继承

# -private fields get a unique scope. # -private 字段获得唯一的范围。 Class hierarchies can be implemented without accidental overwrites of private properties with equal names.可以在不意外覆盖同名私有属性的情况下实现类层次结构。

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"

TS compiler fortunately emits an error, when private properties are in danger of being overwriten (see this example ).幸运的是,当private属性有被覆盖的危险时,TS 编译器会发出错误(请参阅此示例)。 But due to the nature of a compile-time feature everything is still possible at run-time, given compile errors are ignored and/or emitted JS code utilized.但是由于编译时特性的性质,在运行时一切仍然是可能的,因为忽略编译错误和/或使用发出的 JS 代码。

External libraries外部库

Library authors can refactor # -private identifiers without causing a breaking change for clients.库作者可以重构# -private 标识符而不会对客户端造成重大更改。 Library users on the other side are protected from accessing internal fields.另一端的图书馆用户受到保护,无法访问内部字段。

JS API omits # -private fields JS API 省略了# -private 字段

Built-in JS functions and methods ignore # -private fields.内置的 JS 函数和方法会忽略# -private 字段。 This can result in a more predictable property selection at run-time.这可以导致在运行时更可预测的属性选择。 Examples: Object.keys , Object.entries , JSON.stringify , for..in loop and others ( code sample ; see also Matt Bierner's answer ):示例: Object.keysObject.entriesJSON.stringifyfor..in循环等( 代码示例;另见 Matt Bierner 的回答):

class Foo {
    #bar = 42;
    baz = "huhu";
}

Object.keys(new Foo()); // [ "baz" ]

Use cases: private keyword用例: private关键字

Preface:前言:

Access to internal class API and state (compile-time only privacy)访问内部类 API 和状态(仅编译时隐私)

private members of a class are conventional properties at run-time. private成员是运行时的常规属性。 We can use this flexibility to access class internal API or state from the outside.我们可以利用这种灵活性从外部访问类内部 API 或状态。 In order to satisfy compiler checks, mechanisms like type assertions, dynamic property access or @ts-ignore may be used amongst others.为了满足编译器检查,可以使用类型断言、动态属性访问或@ts-ignore等机制。

Example with type assertion ( as / <> ) and any typed variable assignment:带有类型断言 ( as / <> ) 和any类型变量赋值的示例:

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

TS even allows dynamic property access of a private member with an escape-hatch : TS 甚至允许使用escape-hatchprivate成员进行动态属性访问:

class C {
  private foo = 10;
}

const res = new C()["foo"]; // 10, res has type number

Where can private access make sense?私人访问在哪里有意义? (1) unit tests, (2) debugging/logging situations or (3) other advanced case scenarios with project-internal classes (open-ended list). (1) 单元测试,(2) 调试/记录情况或 (3) 其他具有项目内部类的高级案例场景(开放式列表)。

Access to internal variables is a bit contradictory - otherwise you wouldn't have made them private in the first place.访问内部变量有点矛盾——否则你一开始就不会将它们设为private To give an example, unit tests are supposed to be black/grey boxes with private fields hidden as implementation detail.举个例子,单元测试应该是黑/灰框,其中隐藏了私有字段作为实现细节。 In practice though, there may be valid approaches from case to case.但在实践中,可能存在不同情况下的有效方法。

Available in all ES environments适用于所有 ES 环境

TS private modifiers can be used with all ES targets. TS private修饰符可用于所有 ES 目标。 # -private fields are only available for target ES2015 / ES6 or higher. # -private 字段仅适用于target ES2015 / ES6或更高版本。 In ES6+, WeakMap is used internally as downlevel implementation (see here ).在 ES6+ 中, WeakMap在内部用作WeakMap实现(请参阅此处)。 Native # -private fields currently require target esnext . Native # -private 字段当前需要target esnext

Consistency and compatibility一致性和兼容性

Teams might use coding guidelines and linter rules to enforce the usage of private as the only access modifier.团队可能会使用编码指南和 linter 规则来强制使用private作为唯一的访问修饰符。 This restriction can help with consistency and avoid confusion with the # -private field notation in a backwards-compatible manner.此限制有助于保持一致性,并以向后兼容的方式避免与# -private 字段符号混淆。

If required, parameter properties (constructor assignment shorthand) are a show stopper.如果需要, 参数属性(构造函数赋值的简写)是一个阻碍。 They can only be used with private keyword and there are no plans yet to implement them for # -private fields.它们只能与private关键字一起使用,并且还没有计划# -private 字段实现它们。

Other reasons其他原因

  • private might provide better run-time performance in some down-leveling cases (see here ).在某些降级情况下, private可能会提供更好的运行时性能(请参阅此处)。
  • There are no hard private class methods available in TS up to now.到目前为止,TS 中没有可用的硬私有类方法。
  • Some people like the private keyword notation better 😊.有些人更喜欢private关键字符号😊。

Note on both注意两者

Both approaches create some kind of nominal or branded type at compile-time.这两种方法都在编译时创建了某种名义或品牌类型。

class A1 { private a = 0; }
class A2 { private a = 42; }

const a: A1 = new A2(); 
// error: "separate declarations of a private property 'a'"
// same with hard private fields

Also, both allow cross-instance access: an instance of class A can access private members of other A instances:此外,两者都允许跨实例访问:类A的实例可以访问其他A实例的私有成员:

class A {
    private a = 0;
    method(arg: A) {
        console.log(arg.a); // works
    }
}

Sources来源

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 “&”和“ |”之间有什么区别 在打字稿中 - what's the differences between '&' and '|' in typescript 如何在TypeScript中使用私有字段? - How can I use private fields in TypeScript? 关于使用 private 关键字的最佳做法是什么? - What is the best practice regarding the use of the private keyword? TypeScript - 私有变量和受保护变量之间的差异 - TypeScript - Difference between Private and Protected Variables 如何获取 webpack 和 babel 来处理 private typescript 关键字? - How to get webpack and babel to handle private typescript keyword? 当我将 typescript 代码解析为 ast 时,保留关键字“private” - The keyword 'private' is reserved when I parse typescript code into ast Typescript中私有构造函数和受保护构造函数的用途是什么 - What is the usage of Private and Protected Constructors in Typescript 打字稿中的私有静态函数与公共静态函数有什么区别? - What is the difference between a private static function vs. a public static function in typescript? Babel 和 TypeScript 的主要区别是什么 - What are the main differences between Babel and TypeScript 在 angular 中使用 getter 和 setter 以及在属性上使用 private 关键字有什么好处? - What is the advantage of using getters and setters in angular and the private keyword on the property?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM