为什么我无法访问TypeScript私有成员?

时间:2012-10-03 17:24:20

标签: javascript typescript

我正在查看TypeScript中私有成员的实现,我发现它有点令人困惑。 Intellisense不允许访问私有成员,但在纯JavaScript中,它就在那里。这让我觉得TS没有正确实现私有成员。 有什么想法吗?

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

8 个答案:

答案 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获取该链接。

我阅读了他所关联的讨论,这里是关键点的摘要:

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

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

  • 问题是JavaScript本身没有可见性修饰符 - 这不是TypeScript的问题
  • JavaScript社区中已经建立了一种模式:使用下划线为私有属性和方法添加前缀,其中显示“自行承担风险”
  • 当TypeScript设计师说真正的私有属性和方法不是“可能”时,他们的意思是“在我们的设计约束下不可能”,具体来说:
    • 发出的JS是惯用的
    • Boilerplate很小
    • 与普通的JS OOP相比没有额外的开销

答案 5 :(得分:2)

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

enter image description here

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

enter image description here

  

注意:使用javascript可以正常使用,并且两个功能都可以访问   外部。

答案 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";
    }
}

privatepublic放在生成的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'])。其他答案已经涵盖了如何做到这一点。