Javascript 为什么我不应该访问TypeScript私有成员';你不能吗?

Javascript 为什么我不应该访问TypeScript私有成员';你不能吗?,javascript,typescript,Javascript,Typescript,我正在研究TypeScript中私有成员的实现,我发现它有点混乱。Intellisense不允许访问私有成员,但在纯JavaScript中,这一切都在那里。这让我觉得TS没有正确地实现私有成员。 有什么想法吗 class Test{ private member: any = "private member"; } alert(new Test().member); 与类型检查一样,成员的隐私仅在编译器中强制执行 私有属性作为常规属性实现,不允许类外的代码访问它 要使类内的某些内容真正私有

我正在研究TypeScript中私有成员的实现,我发现它有点混乱。Intellisense不允许访问私有成员,但在纯JavaScript中,这一切都在那里。这让我觉得TS没有正确地实现私有成员。 有什么想法吗

class Test{
  private member: any = "private member";
}
alert(new Test().member);

与类型检查一样,成员的隐私仅在编译器中强制执行

私有属性作为常规属性实现,不允许类外的代码访问它


要使类内的某些内容真正私有,它不能是类的成员,而是在创建对象的代码内的函数范围内创建的局部变量。这意味着您不能像类的成员一样访问它,即使用
this
关键字。

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++;
        }
    }
}
编辑

这种方法只能在绝对需要的情况下少量使用。例如,如果需要临时缓存密码

使用此模式会带来性能成本(与Javascript或Typescript无关),并且只应在绝对必要的情况下使用

一旦对的支持变得更广泛,就会有一种有趣的技术在示例#3中详细介绍

它允许私有数据,并通过允许从原型方法而不是仅从实例方法访问数据,避免了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)();
    }
  }
}
let_counter=new WeakMap();
让_action=new WeakMap();
班级倒计时{
构造函数(计数器、动作){
_设置(这个,计数器);
_动作。设定(这个动作);
}
减量{
让计数器=_counter.get(this);
如果(计数器<1)返回;
计数器--;
_设置(这个,计数器);
如果(计数器==0){
_行动。得到(这个)();
}
}
}

在TypeScript中,只能在类内部访问私有函数。像

当您尝试访问私有成员时,它将显示一个错误。以下是一个例子:

注意:使用javascript就可以了,两个函数都可以访问 外面


感谢Sean Feldman提供有关此问题的官方讨论的链接-请参阅链接

我阅读了他链接到的讨论,这里是关键点的摘要:

  • 建议:构造函数中的私有属性
    • 问题:无法从原型函数访问
  • 建议:构造函数中的私有方法
    • 问题:与属性相同,而且在原型中每个类创建一次函数会失去性能优势;而是为每个实例创建函数的副本
  • 建议:为抽象属性访问添加样板文件,并加强可见性
    • 问题:主要性能开销;TypeScript是为大型应用程序设计的
  • 建议:TypeScript已经将构造函数和原型方法定义封装在闭包中;将私有方法和属性放在那里
    • 在闭包中放置私有属性的问题:它们成为静态变量;每个实例没有一个
    • 在该闭包中放置私有方法的问题:如果没有某种解决方法,它们无法访问
      this
  • 建议:自动损坏私有变量名
    • 反驳:这是一种命名约定,不是一种语言构造。你自己把它弄坏
  • 建议:使用
    @private
    注释私有方法,这样识别该注释的缩微器可以有效地缩小方法名称
    • 对这一点没有重要的反驳
在发出的代码中添加可见性支持的总体计数器参数:

  • 问题是JavaScript本身没有可见性修饰符——这不是TypeScript的问题
  • JavaScript社区中已经有一种既定模式:在私有属性和方法前面加下划线,表示“自行承担风险”
  • 当TypeScript设计者说真正的私有属性和方法“不可能”时,他们的意思是“在我们的设计约束下不可能”,具体来说:
    • 发出的JS是惯用的
    • 样板是最小的
    • 与普通JS OOP相比,没有额外的开销

我意识到这是一个较老的讨论,但分享我对TypeScript中假定的私有变量和方法“泄漏”到已编译JavaScript类的公共接口的问题的解决方案可能仍然有用

对我来说,这个问题纯粹是表面现象,也就是说,当在DevTools中查看一个实例变量时,它是关于视觉混乱的。我的解决方案是将私有声明分组到另一个类中,然后在主类中实例化,并分配给一个名为
\uuu
(但在JS中仍然可见)的
私有变量(双下划线)

例如:

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中查看
myClass
实例时,您不会看到它的所有“私有”成员与真正的公共成员混杂在一起(在经过适当重构的真实代码中,这些成员在视觉上会变得非常混乱),而是会看到它们整齐地组合在折叠的
\uuuu
属性中:


由于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.
私有字段以
#
字符开头

请注意,这些私有字段将有所不同
/**
 * 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);
    }
}
// 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;
    }
}
class Test{
  private member: any = "private member";
}
alert(new Test().member);
class Test{
  private member: any = "private member";
}
alert(new Test().member);
class Test2 {
    constructor() {
        var test = new Test();
        test.member = "Cannot do this";
    }
}
var Test = (function () {
    function Test() {
        this.member = "private member";
    }
    return Test1;
}());
alert(new Test().member);