为什么我不能访问 TypeScript 私有成员?

IT技术 javascript typescript
2021-01-26 10:25:37

我正在研究 TypeScript 中私有成员的实现,我发现它有点令人困惑。Intellisense 不允许访问私有成员,但在纯 JavaScript 中,一切都在那里。这让我认为 TS 没有正确实现私有成员。有什么想法吗?

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

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

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

为了使类内部的某些东西真正私有,它不能是类的成员,它是在创建对象的代码内的函数作用域内创建的局部变量。这意味着您不能像类的成员那样访问它,即使用this关键字。

这是我一直在提供反馈的事情。我相信它应该提供创建显示module模式的选项,因此私有成员可以保持私有,而公共成员可以在 JavaScript 中访问。这是一种常见模式,可在 TS 和 JS 中提供相同的可访问性。
2021-03-12 10:25:37
@BasaratAli:这是一个在类的方法中可用的静态变量,但它不是类的成员,即您不能使用this关键字访问它
2021-03-16 10:25:37
@Eric:由于 TypeScript 使用方法原型而不是在构造函数中添加方法作为原型,因此无法从方法访问构造函数中的局部变量。可能可以在类的函数包装器内创建一个局部变量,但我还没有找到一种方法来做到这一点。但是,这仍然是一个局部变量,而不是私有成员。
2021-03-21 10:25:37
有一个可以用于私有静态成员的解决方案:web.archive.org/web/20140521231105/https : //basarat.com/2013/03/...
2021-03-31 10:25:37
javascript 程序员将局部变量放入对象构造函数并将其用作私有字段并不罕见。我很惊讶他们不支持这样的事情。
2021-04-06 10:25:37

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 无关),只能在绝对必要的情况下使用。

typescript不是一直通过设置var _this在作用域函数中使用来做到这一点吗?为什么你会在课堂范围内感到不安?
2021-03-13 10:25:37
不。 var _this 只是对此的引用。
2021-03-19 10:25:37
哦,是的,抱歉,问题出在另一个,事实上,对于您创建的每个实例,都会再次创建 doSomething,因为它不是原型链的一部分。
2021-03-19 10:25:37
@BarbuBarbu 是的,我同意。这是这种方法的一个大问题,也是应该避免的原因之一。
2021-03-26 10:25:37
更准确地称它们为构造函数变量,而不是私有变量。这些在原型方法中是不可见的。
2021-04-01 10:25:37

由于 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/annoucing-typescript-3-8-beta/

一旦对WeakMap 的支持更加广泛,这里的示例 #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)();
    }
  }
}
@RamtinSoltani 所以删除你的旧评论?>
2021-03-19 10:25:37
从MDN页:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...相比之下,原生 WeakMap 持有对关键对象的“弱”引用,这意味着它们不会阻止垃圾收集,以防没有其他对关键对象的引用。这也避免了防止对地图中的值进行垃圾收集。
2021-03-20 10:25:37
@RyanThomas 是的,那是我不久前留下的旧评论。WeakMaps 与 Maps 不同,不会导致内存泄漏。所以使用这种技术是安全的。
2021-03-31 10:25:37
@RamtinSoltani 链接的文章统计表明,由于弱图的工作方式,这不会阻止垃圾收集。如果有人想在使用这种技术时更加安全,他们可以实现自己的处理代码,从每个弱映射中删除类实例键。
2021-04-04 10:25:37
我喜欢它!基本上它意味着将私有属性隐藏到聚合类中。最有趣的将是... 添加对protected参数的支持如何:D
2021-04-06 10:25:37

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

我阅读了他链接到的讨论,以下是要点摘要:

  • 建议:构造函数中的私有属性
    • 问题:无法从原型函数访问
  • 建议:构造函数中的私有方法
    • 问题:与属性相同,另外你失去了在原型中为每个类创建一个函数的性能优势;相反,您为每个实例创建函数的副本
  • 建议:添加样板以抽象属性访问并强制执行可见性
    • 问题:主要的性能开销;TypeScript 是为大型应用程序设计的
  • 建议: TypeScript 已经将构造函数和原型方法定义包装在一个闭包中;将私有方法和属性放在那里
    • 将私有属性放入该闭包的问题:它们变成静态变量;每个实例没有一个
    • 将私有方法放入该闭包的问题:如果没有this某种解决方法,它们将无法访问
  • 建议:自动修改私有变量名
    • 反论点:这是命名约定,而不是语言结构。自己动手
  • 建议:使用@private识别注释可以有效地缩小方法名称的缩小器对私有方法进行注释
    • 对此没有重要的反驳论据

在发出的代码中添加可见性支持的总体反驳:

  • 问题是 JavaScript 本身没有可见性修饰符——这不是 TypeScript 的问题
  • JavaScript 社区中已经有一个既定的模式:在私有属性和方法前面加上下划线,表示“风险自担”
  • 当 TypeScript 设计师说真正的私有属性和方法不是“可能的”时,他们的意思是“在我们的设计约束下不可能”,特别是:
    • 发出的 JS 是惯用的
    • 样板是最小的
    • 与普通的 JS OOP 相比没有额外的开销
是的,这就是对话 - 但我链接到Sean Feldman 对这个问题的回答,他提供了链接。由于他做了寻找链接的工作,我想给他功劳。
2021-03-19 10:25:37
如果此答案来自此对话:typescript.codeplex.com/discussions/397651 - ,请提供链接:D
2021-03-25 10:25:37