Javascript 为什么我不能访问 TypeScript 私有成员?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12713659/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why can I access TypeScript private members when I shouldn't be able to?
提问by Sean Feldman
I'm looking at implementation of private members in TypeScript, and I find it a little confusing. Intellisense doesn't allow to access private member, but in pure JavaScript, it's all there. This makes me think that TS doesn't implement private members correctly. Any thoughts?
我正在研究 TypeScript 中私有成员的实现,我发现它有点令人困惑。Intellisense 不允许访问私有成员,但在纯 JavaScript 中,一切都在那里。这让我认为 TS 没有正确实现私有成员。有什么想法吗?
class Test{
private member: any = "private member";
}
alert(new Test().member);
采纳答案by Guffa
Just as with the type checking, the privacy of members are only enforced within the compiler.
与类型检查一样,成员的隐私仅在编译器中强制执行。
A private property is implemented as a regular property, and code outside the class is not allowed to access it.
私有属性实现为常规属性,不允许类外的代码访问它。
To make something truly private inside the class, it can't be a member of the class, it would be a local variable created inside a function scope inside the code that creates the object. That would mean that you can't access it like a member of the class, i.e. using the this
keyword.
为了使类内部的某些东西真正私有,它不能是类的成员,它是在创建对象的代码内的函数作用域内创建的局部变量。这意味着您不能像类的成员那样访问它,即使用this
关键字。
回答by Martin
JavaScript does support private variables.
JavaScript 确实支持私有变量。
function MyClass() {
var myPrivateVar = 3;
this.doSomething = function() {
return myPrivateVar++;
}
}
In TypeScript this would be expressed like so:
在 TypeScript 中,这可以这样表达:
class MyClass {
doSomething: () => number;
constructor() {
var myPrivateVar = 3;
this.doSomething = function () {
return myPrivateVar++;
}
}
}
EDIT
编辑
This approach should only be used SPARINGLYwhere it is absolutely needed. For example if you need to cache a password temporarily.
这种方法只能用于少量的地方是绝对必要的。例如,如果您需要临时缓存密码。
There are performance costs to using this pattern (irrelevant of Javascript or Typescript) and should only be used where absolutely necessary.
使用这种模式会产生性能成本(与 Javascript 或 Typescript 无关),只能在绝对必要的情况下使用。
回答by Ryan Thomas
Once support for WeakMapis more widely available there is an interesting technique detailed in example #3 here.
一旦对WeakMap 的支持更加广泛,这里的示例 #3 中详细介绍了一种有趣的技术。
It allows for private data AND avoids the performance costs of Jason Evans example by allowing the data to be accessible from prototype methods instead of only instance methods.
它允许私有数据并通过允许从原型方法而不是仅实例方法访问数据来避免 Jason Evans 示例的性能成本。
The linked MDN WeakMap page lists browser support at Chrome 36, Firefox 6.0, IE 11, Opera 23, and Safari 7.1.
链接的 MDN WeakMap 页面列出了 Chrome 36、Firefox 6.0、IE 11、Opera 23 和 Safari 7.1 的浏览器支持。
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)();
}
}
}
回答by Przemek Struciński
Since TypeScript 3.8 will be released you will be able to declare private field which can't be accessed or even detected outside of the containing class.
由于 TypeScript 3.8 将发布,您将能够声明在包含 class 之外无法访问甚至无法检测到的私有字段 。
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.
Private fields starts with #
character
私有字段以#
字符开头
Please note that these private fields will be something different than fields marked with private
keyword
请注意,这些私有字段将与用private
关键字标记的字段不同
Ref. https://devblogs.microsoft.com/typescript/announcing-typescript-3-8-beta/
参考 https://devblogs.microsoft.com/typescript/annoucing-typescript-3-8-beta/
回答by alexanderbird
Thanks to Sean Feldman for the link to the official discussion on this issue - see his answerfor the link.
感谢 Sean Feldman 提供有关此问题的官方讨论的链接 - 请参阅他对链接的回答。
I read the discussion he linked to, and here's a summary of the key points:
我阅读了他链接到的讨论,以下是要点摘要:
- Suggestion:private properties in constructor
- problems:can't access from prototype functions
- Suggestion:private methods in constructor
- problems:same as with properties, plus you lose the performance benefit of creating a function once per class in the prototype; instead you create a copy of the function for each instance
- Suggestion:add boilerplate to abstract property access and enforce visibility
- problems:major performance overhead; TypeScript is designed for large applications
- Suggestion:TypeScript already wraps the constructor and prototype method definitions in a closure; put private methods and properties there
- problems with putting private properties in that closure:they become static variables; there is not one per instance
- problems with putting private methods in that closure:they do not have access to
this
without some sort of workaround
- Suggestion:automatically mangle the private variable names
- counter arguments:that's a naming convention, not a language construct. Mangle it yourself
- Suggestion:Annotate private methods with
@private
so minifiers that recognize that annotation can effectively minify the method names- No significant counter arguments to this one
- 建议:构造函数中的私有属性
- 问题:无法从原型函数访问
- 建议:构造函数中的私有方法
- 问题:与属性相同,而且你失去了在原型中为每个类创建一个函数的性能优势;相反,您为每个实例创建函数的副本
- 建议:添加样板以抽象属性访问并强制执行可见性
- 问题:主要的性能开销;TypeScript 是为大型应用程序设计的
- 建议:TypeScript 已经将构造函数和原型方法定义包装在一个闭包中;将私有方法和属性放在那里
- 将私有属性放入该闭包的问题:它们变成静态变量;每个实例没有一个
- 将私有方法放入该闭包的问题:如果没有
this
某种解决方法,它们将无法访问
- 建议:自动修改私有变量名
- 反论点:这是命名约定,而不是语言结构。自己动手
- 建议:使用
@private
识别注释可以有效地缩小方法名称的缩小器对私有方法进行注释- 对此没有重要的反驳论据
Overall counter-arguments to adding visibility support in emitted code:
在发出的代码中添加可见性支持的总体反驳:
- the problem is that JavaScript itself doesn't have visibility modifiers - this isn't TypeScript's problem
- there is already an established pattern in the JavaScript community: prefix private properties and methods with an underscore, which says "proceed at your own risk"
- when TypeScript designers said that truly private properties and methods aren't "possible", they meant "not possible under our design constraints", specifically:
- The emitted JS is idiomatic
- Boilerplate is minimal
- No additional overhead compared to normal JS OOP
- 问题是 JavaScript 本身没有可见性修饰符——这不是 TypeScript 的问题
- JavaScript 社区中已经有一个既定的模式:在私有属性和方法前面加上下划线,表示“风险自担”
- 当 TypeScript 设计者说真正的私有属性和方法不是“可能的”时,他们的意思是“在我们的设计约束下不可能”,特别是:
- 发出的 JS 是惯用的
- 样板是最小的
- 与普通的 JS OOP 相比没有额外的开销
回答by Muhammad Awais
In TypeScript Private functions are only accessible inside the class. Like
在 TypeScript 中,私有函数只能在类内部访问。喜欢
And it will show an error when you try to access a private member. Here is the example:
当您尝试访问私人成员时,它会显示错误。这是示例:
Note: It will be fine with javascript and both function are accessible outside.
注意:使用 javascript 就可以了,并且这两个函数都可以在外部访问。
回答by Caspian Canuck
I realize this is an older discussion but it might still be useful to share my solution to the problem of the supposedly private variables and methods in a TypeScript "leaking" out into the public interface of the compiled JavaScript class.
我意识到这是一个较旧的讨论,但分享我对 TypeScript 中所谓的私有变量和方法“泄漏”到已编译 JavaScript 类的公共接口的问题的解决方案可能仍然有用。
To me this issue is purely cosmetic, i.e. it's all about the visual clutter when an instance variable is viewed in DevTools. My fix is to group private declarations together inside another class that is then instantiated in the main class and assigned to a private
(but still publicly visible in JS) variable with a name like __
(double underscore).
对我来说,这个问题纯粹是装饰性的,即当在 DevTools 中查看实例变量时,这完全是关于视觉混乱。我的解决方法是在另一个类中将私有声明组合在一起,然后在主类中实例化并分配给一个private
(但在 JS 中仍然公开可见)变量,其名称类似于__
(双下划线)。
Example:
例子:
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);
When the myClass
instance is viewed in DevTools, instead of seeing all its "private" members intermixed with truly public ones (which can get very visually messy in properly refactored real-life code) you see them neatly grouped inside the collapsed __
property:
当myClass
在 DevTools 中查看实例时,您不会看到它的所有“私有”成员与真正的公共成员混合在一起(在正确重构的实际代码中可能会在视觉上变得非常混乱),您会看到它们整齐地分组在折叠__
属性中:
回答by vitaly-t
Here's reusable approach for adding proper private properties:
这是添加适当私有属性的可重用方法:
/**
* Implements proper private properties.
*/
export class Private<K extends object, V> {
private propMap = new WeakMap<K, V>();
get(obj: K): V {
return this.propMap.get(obj)!;
}
set(obj: K, val: V) {
this.propMap.set(obj, val);
}
}
Let's say you have class Client
somewhere that needs two private properties:
假设您在Client
某个地方有一个需要两个私有属性的类:
prop1: string
prop2: number
prop1: string
prop2: number
Below is how you implement it:
下面是你如何实现它:
// our private properties:
interface ClientPrivate {
prop1: string;
prop2: number;
}
// private properties for all Client instances:
const pp = new Private<Client, ClientPrivate>();
class Client {
constructor() {
pp.set(this, {
prop1: 'hello',
prop2: 123
});
}
someMethod() {
const privateProps = pp.get(this);
const prop1 = privateProps.prop1;
const prop2 = privateProps.prop2;
}
}
And if all you need is a single private property, then it gets even simpler, because you would not need to define any ClientPrivate
in that case.
如果您只需要一个私有属性,那么它会变得更简单,因为ClientPrivate
在这种情况下您不需要定义任何属性。
Worth noting, that for the most part, class Private
just offers a nicely readable signature, whereas direct use of WeakMap
does not.
值得注意的是,在大多数情况下, classPrivate
只是提供了一个很好读的签名,而直接使用 ofWeakMap
则没有。