Why can I access TypeScript private members when I shouldn't be able to?
我正在研究TypeScript中私有成员的实现,但发现它有些令人困惑。 Intellisense不允许访问私有成员,但是在纯JavaScript中,仅此而已。 这使我认为TS无法正确实现私有成员。
有什么想法吗?
1 2 3 4 | class Test{ private member: any ="private member"; } alert(new Test().member); |
就像类型检查一样,成员的隐私仅在编译器中强制执行。
私有属性被实现为常规属性,并且不允许类外的代码对其进行访问。
为了使某些东西真正成为类内的私有对象,它不能成为该类的成员,而应是在创建对象的代码内的函数作用域内创建的局部变量。这意味着您不能像类的成员一样访问它,即使用
JavaScript确实支持私有变量。
1 2 3 4 5 6 7 | function MyClass() { var myPrivateVar = 3; this.doSomething = function() { return myPrivateVar++; } } |
在TypeScript中,它表示为:
1 2 3 4 5 6 7 8 9 10 11 12 | class MyClass { doSomething: () => number; constructor() { var myPrivateVar = 3; this.doSomething = function () { return myPrivateVar++; } } } |
编辑
仅应在绝对需要时谨慎使用此方法。例如,如果您需要临时缓存密码。
使用这种模式会降低性能(与Javascript或Typescript无关),仅应在绝对必要的情况下使用。
由于TypeScript 3.8将发布,因此您将能够声明私有字段,该私有字段在包含类之外无法访问甚至无法检测到。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class Person { #name: string constructor(name: string) { this.#name = name; } greet() { console.log(`Hello, my name is ${this.#name}!`); } } let jeremy = new Person("Jeremy Bearimy"); jeremy.#name // ~~~~~ // Property '#name' is not accessible outside class 'Person' // because it has a private identifier. |
专用字段以
请注意,这些私有字段与标有
参考https://devblogs.microsoft.com/typescript/announcing-typescript-3-8-beta/
一旦对WeakMap的支持更加广泛,这里将在示例3中详细介绍一种有趣的技术。
它允许私有数据,并通过允许从原型方法而不是实例方法访问数据来避免Jason Evans示例的性能损失。
链接的MDN WeakMap页面列出了Chrome 36,Firefox 6.0,IE 11,Opera 23和Safari 7.1对浏览器的支持。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | let _counter = new WeakMap(); let _action = new WeakMap(); class Countdown { constructor(counter, action) { _counter.set(this, counter); _action.set(this, action); } decrement() { let counter = _counter.get(this); if (counter < 1) return; counter--; _counter.set(this, counter); if (counter === 0) { _action.get(this)(); } } } |
感谢Sean Feldman提供的关于此问题的正式讨论的链接-请参阅他的答案作为链接。
我阅读了他链接的讨论,这是关键点的摘要:
-
建议:构造函数中的私有属性
- 问题:无法从原型函数访问
-
建议:构造函数中的私有方法
- 问题:与属性相同,此外,您还失去了在原型中为每个类创建一次函数的性能优势;而是为每个实例创建函数的副本
-
建议:添加样板以抽象属性访问并增强可见性
- 问题:主要的性能开销; TypeScript专为大型应用程序而设计
-
建议:TypeScript已经将构造函数和原型方法定义包装在一个闭包中。将私有方法和属性放在那里
- 将私有属性放在该闭包中的问题:它们成为静态变量;每个实例没有一个
-
在该闭包中放置私有方法的问题:如果没有某种解决方法,它们将无法访问
this
-
建议:自动修改私有变量名称
- 反参数:这是命名约定,不是语言构造。自己弄乱
-
建议:使用
@private 注释私有方法,以便识别该注释的压缩程序可以有效地缩小方法名称- 对此没有重大反驳
在发出的代码中增加可见性支持的总体反论点:
- 问题是JavaScript本身没有可见性修饰符-这不是TypeScript的问题
- JavaScript社区中已经建立了一个模式:在私有属性和方法前加下划线,表示"后果自负"。
-
当TypeScript设计师说真正的私有属性和方法不是"可能的"时,他们的意思是"在我们的设计约束下是不可能的",特别是:
- 发出的JS是惯用的
- 样板最小
- 与普通JS OOP相比,没有额外的开销
我意识到这是一个较旧的讨论,但是在TypeScript中"泄漏"到已编译的JavaScript类的公共接口中时,解决我的私有变量和方法的问题可能仍然有用。
对我来说,这个问题纯粹是表面上的问题,即与在DevTools中查看实例变量时的视觉混乱有关。我的解决方法是将私有声明分组到另一个类中,然后在主类中将其实例化并分配给名称为
例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | class Privates { readonly DEFAULT_MULTIPLIER = 2; foo: number; bar: number; someMethod = (multiplier: number = this.DEFAULT_MULTIPLIER) => { return multiplier * (this.foo + this.bar); } private _class: MyClass; constructor(_class: MyClass) { this._class = _class; } } export class MyClass { private __: Privates = new Privates(this); constructor(foo: number, bar: number, baz: number) { // assign private property values... this.__.foo = foo; this.__.bar = bar; // assign public property values... this.baz = baz; } baz: number; print = () => { console.log(`foo=${this.__.foo}, bar=${this.__.bar}`); console.log(`someMethod returns ${this.__.someMethod()}`); } } let myClass = new MyClass(1, 2, 3); |
当在DevTools中查看
这是添加适当的私有属性的可重用方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /** * Implements proper private properties. */ export class Private;lt;K extends object, V;gt; { private propMap = new WeakMap;lt;K, V;gt;(); get(obj: K): V { return this.propMap.get(obj)!; } set(obj: K, val: V) { this.propMap.set(obj, val); } } |
假设您在某个地方需要两个私有属性的类
-
prop1: string -
prop2: number
以下是您的实现方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // our private properties: interface ClientPrivate { prop1: string; prop2: number; } // private properties for all Client instances: const pp = new Private;lt;Client, ClientPrivate;gt;(); class Client { constructor() { pp.set(this, { prop1: 'hello', prop2: 123 }); } someMethod() { const privateProps = pp.get(this); const prop1 = privateProps.prop1; const prop2 = privateProps.prop2; } } |
而且,如果您只需要一个私有属性,则它变得更加简单,因为在这种情况下您无需定义任何
值得一提的是,在大多数情况下,类
在TypeScript中,私有函数只能在类内部访问。喜欢
当您尝试访问私有成员时,它将显示错误。这是示例:
Note: It will be fine with javascript and both function are accessible
outside.