在继续之前,我想指出,我已经问了一些关于 TypeScript、它的编译器、它在其整个生命周期中已经实现和尚未实现的目标以及 1.0 版本路线图的问题
这个问题涉及 TypeScript 中
public
和 private
关键字的使用,以及它们与编译的 JavaScript 有何关系。
考虑以下 TypeScript 类:
class Example {
private messageA: string;
public messageB: string;
constructor(message?: string) {
this.messageA = "private: " + message;
this.messageB = "public: " + message;
}
public showMessageA(): void {
alert(this.messageA);
}
private showMessageB(): void {
alert(this.messageB);
}
}
var example = new Example("Hello World");
现在,当我输入
example
时。智能感知 (TypeScript) 告诉我可以访问 messageB
和 showMessageA
,因为它们都是 public
。然而,这种行为(虽然可能)在编译后的 JavaScript 中并不明显。
这是我的类的 JavaScript 编译:
var Example = (function () {
function Example(message) {
this.messageA = "private: " + message;
this.messageB = "public: " + message;
}
Example.prototype.showMessageA = function () {
alert(this.messageA);
};
Example.prototype.showMessageB = function () {
alert(this.messageB);
};
return Example;
})();
var example = new Example("Hello World");
现在,如果我将此示例粘贴到浏览器控制台(我使用的是 Chrome),我可以访问
messageA
、messageB
、showMessageA
、showMessageB
,这意味着在 JavaScript 中,所有访问修饰符都将被忽略。
我个人认为这是错误的! JavaScript 能够对访问修饰符进行建模,因此我相信 TypeScript 也应该效仿。
考虑以下手写 JavaScript,它正确地模拟了
private
和 public
变量和函数:
var Example = (function() {
return function Example(message) {
var messageA = "private: " + message;
this.messageB = "public: " + message;
this.showMessageA = function() {
alert(messageA);
}
var showMessageB = function() {
alert(this.messageB);
}
}
})();
var example = new Example("Hello World");
现在,如果我将此示例粘贴到浏览器控制台中,我只能访问
messageB
和 showMessageA
,这与我尝试使用 TypeScript 实现的目标是正确的。
问题
使用闭包来模拟私有访问的问题是每个实例都需要每个方法的自己的副本。这意味着每次创建实例时,都必须编译每个方法函数,并且必须为新函数保留内存空间。这并不理想,也不是 TypeScript 想要实现的目标。
从语义上讲,您描述的方法即闭包模型在很多方面都优于将方法放在原型上的 JavaScript 模型——即使对于公共方法也是如此。 EcmaScript 6 委员会的一些人(TypeScript 正是基于该委员会的类系统)更喜欢这样的类系统。事实上,TypeScript 的早期内部版本就实现了这样的模型。
不幸的是,这样的模型在 JavaScript 中无法变得高效。它通常要求每个方法都被分配为每个对象实例的单独闭包——即,创建单个对象实例将涉及创建许多函数对象。由于 JavaScript 对
this
的处理极其薄弱,及其函数标识的概念,一般来说没有简单的方法可以优化这些分配。
因此,EcmaScript 6 确定了当前模型(尽管目前只有公共模型),而 TypeScript 遗憾地紧随其后(公共和私有)。然后,私有成员仅在 TypeScript 中提供静态检查,没有封装保证。
FWIW,在此模型中提供私有的正确方法是私有符号(又名私有名称),但不幸的是,这些没有进入 ES6,并且无论如何都不是 TypeScript 的选择。