我正在查看TypeScript中私有成员的实现,我发现它有点令人困惑。 Intellisense不允许访问私有成员,但在纯JavaScript中,它就在那里。这让我觉得TS没有正确实现私有成员。 有什么想法吗?
class Test{
private member: any = "private member";
}
alert(new Test().member);
答案 0 :(得分:85)
与类型检查一样,成员的隐私仅在编译器中强制执行。
私有属性是作为常规属性实现的,并且不允许该类外的代码访问它。
要在类中创建真正私有的东西,它不能是类的成员,它将是在创建对象的代码内的函数作用域内创建的局部变量。这意味着您无法像使用this
关键字的成员那样访问它。
答案 1 :(得分:36)
JavaScript确实支持私有变量。
function MyClass() {
var myPrivateVar = 3;
this.doSomething = function() {
return myPrivateVar++;
}
}
在TypeScript中,这将表达如下:
class MyClass {
doSomething: () => number;
constructor() {
var myPrivateVar = 3;
this.doSomething = function () {
return myPrivateVar++;
}
}
}
修改强>
这种方法只能在绝对需要的地方使用 SPARINGLY 。例如,如果您需要临时缓存密码。
使用此模式会产生性能成本(与Javascript或Typescript无关),只能在绝对必要的地方使用。
答案 2 :(得分:12)
一旦对WeakMap的支持得到更广泛的支持,就会在示例#3 here中详细介绍一种有趣的技术。
它允许私有数据并通过允许从原型方法而不仅仅是实例方法访问数据来避免Jason Evans示例的性能成本。
链接的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)();
}
}
}
答案 3 :(得分:12)
自从TypeScript 3.8发布以来,您将能够声明私有字段,该私有字段在包含的类之外甚至无法访问。
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
关键字的字段有所不同
参考https://devblogs.microsoft.com/typescript/announcing-typescript-3-8-beta/
答案 4 :(得分:3)
感谢Sean Feldman提供有关此问题的官方讨论的链接 - 请参阅his answer获取该链接。
我阅读了他所关联的讨论,这里是关键点的摘要:
this
@private
注释私有方法,以便识别注释可以有效地缩小方法名称的缩小器
在发出的代码中添加可见性支持的总体反驳论据:
答案 5 :(得分:2)
答案 6 :(得分:0)
这里是可重复使用的方法,用于添加适当的私有属性:
/**
* 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);
}
}
假设您在某个需要两个私有属性的地方有Client
类,
prop1: string
prop2: number
以下是实现方式:
// 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;
}
}
如果您只需要一个私有属性,那么它将变得更加简单,因为在这种情况下您无需定义任何ClientPrivate
。
值得一提的是,在大多数情况下,类Private
仅提供了易于阅读的签名,而直接使用WeakMap
则没有。
答案 7 :(得分:0)
实际上,这个问题的答案很简单。
您有以下代码:
class Test{
private member: any = "private member";
}
alert(new Test().member);
在该代码中,您正在混合两种不同的语言。这是TypeScript:
class Test{
private member: any = "private member";
}
这是JavaScript:
alert(new Test().member);
private
类别的Test
类中的member
关键字用于TypeScript。因此,TypeScript中的其他类无法访问member
字段,因为TypeScript编译器不允许它。例如,如果您尝试过此操作,则将无效:
class Test2 {
constructor() {
var test = new Test();
test.member = "Cannot do this";
}
}
将private
或public
放在生成的JavaScript上都没有区别。生成的JavaScript代码将始终为:
var Test = (function () {
function Test() {
this.member = "private member";
}
return Test1;
}());
因此,您可以执行此操作,因为JavaScript允许这样做:
alert(new Test().member);
这不是刻板的规则,可能有些情况我不知道,但是如果您使用的是TypeScript,那为什么还要担心允许使用JavaScript做什么呢?这个想法是,您不再编写JavaScript,因此您在JavaScript中可以/不能做的事情不再值得担心。对我而言,这种担心与编写C#代码,然后说出我如何能够更改CIL或汇编语言的私有字段相同。我不确定CIL是否允许,但这不是重点。关键是,在用C#编写代码之后,您只是不去研究CIL可以做什么。
在某些情况下,您可能会使用TypeScript编写代码,但公众可能会使用生成的JavaScript,可能是一个插件,并且您不希望它们破坏事情,在这种情况下,您会为此担心。在这种情况下,即使在JavaScript端,您也可以编写TypeScript代码以使字段private
成为groupby(['Date', 'Activity'])
。其他答案已经涵盖了如何做到这一点。