114

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

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

9 回答 9

105

就像类型检查一样,成员的隐私只在编译器内强制执行。

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

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

于 2012-10-03T17:36:58.397 回答
38

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

于 2014-06-06T16:01:00.610 回答
21

由于 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/

于 2020-01-13T07:40:44.850 回答
11

一旦对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)();
    }
  }
}
于 2016-04-10T04:23:32.557 回答
5

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

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

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

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

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

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

对我来说,这个问题纯粹是装饰性的,也就是说,当在 DevTools 中查看实例变量时,视觉上的杂乱无章。我的解决方法是将私有声明组合在另一个类中,然后在主类中实例化并分配给一个private(但在 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);

myClass在 DevTools 中查看该实例时,您不会看到它的所有“私有”成员与真正的公共成员混合在一起(在正确重构的真实代码中可能会在视觉上变得非常混乱),而是看到它们整齐地分组在折叠__属性中:

在此处输入图像描述

于 2018-09-14T22:12:51.887 回答
0

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

在此处输入图像描述

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

在此处输入图像描述

注意:使用 javascript 就可以了,并且这两个函数都可以在外部访问。

于 2016-07-15T07:33:28.783 回答
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

值得注意的是,在大多数情况下, classPrivate只是提供了一个可读性很好的签名,而直接使用 ofWeakMap却没有。

于 2020-03-12T06:22:01.487 回答
-2

其实这个问题的答案很简单。

你有这个代码:

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关键字用于 TypeScript。因此 TypeScript 中的其他类无法访问该字段,因为 TypeScript 编译器不允许它。例如,如果您尝试这样做,它将无法正常工作:Testmembermember

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,也许是一个插件,并且您不希望他们破坏事物,那么在这种情况下您会担心它。对于这些情况,您将编写 TypeScript 代码以使字段private甚至在 JavaScript 端。其他答案已经涵盖了如何做到这一点。

于 2020-12-07T18:05:57.183 回答