Javascript 使用TypeScript编译类的观察
在我继续之前,我想指出,我已经问了一些关于TypeScript的问题,它的编译器,以及它在其整个生命周期和走向版本1.0的路线图中已经实现了什么和没有实现什么 这个问题涉及在TypeScript中使用Javascript 使用TypeScript编译类的观察,javascript,class,compiler-construction,typescript,access-modifiers,Javascript,Class,Compiler Construction,Typescript,Access Modifiers,在我继续之前,我想指出,我已经问了一些关于TypeScript的问题,它的编译器,以及它在其整个生命周期和走向版本1.0的路线图中已经实现了什么和没有实现什么 这个问题涉及在TypeScript中使用public和private关键字,以及这些关键字与已编译JavaScript的关系 考虑以下类型脚本类: class Example { private messageA: string; public messageB: string; constructor(mess
public
和private
关键字,以及这些关键字与已编译JavaScript的关系
考虑以下类型脚本类:
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");
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");
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");
现在,当我键入示例时。intellisense(TypeScript)告诉我,我可以访问messageB
和showMessageA
,因为它们都是公共的
。但是,这种行为(虽然可能)在编译的JavaScript中并不明显
这是我的类的JavaScript编译:
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");
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");
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");
现在,如果我将这个示例粘贴到我的浏览器控制台(我使用的是Chrome),我可以访问messageA
,messageB
,showMessageA
,showMessageB
,这意味着在JavaScript中,所有访问修饰符都被忽略
我个人认为这是错误的!JavaScript能够为访问修饰符建模,因此我认为TypeScript也应该效仿
考虑以下手工编写的JavaScript,它对private
和public
变量和函数进行了正确建模:
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");
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");
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时忽略访问修饰符
为什么TypeScript将所有方法绑定到原型,而不是基于每个实例
与我的自定义实现相比,如果TypeScript编译类的方式有什么优点,那是什么,为什么
使用闭包来模拟私有访问的问题是每个实例都需要每个方法的副本。这意味着每次创建实例时,都必须编译每个方法函数,并且必须为新函数保留内存空间。这并不理想,也不是TypeScript试图实现的目标。从语义上讲,您描述的方法作为闭包模型在许多方面都优于JavaScript将方法放在原型上的模型——甚至对于公共方法也是如此。EcmaScript 6委员会(TypeScript是其类系统的基础)中的一些人会倾向于采用类似的类系统。事实上,TypeScript的早期内部版本实现了这样一个模型
不幸的是,这样的模型在JavaScript中无法变得高效。它通常需要为每个对象实例将每个方法分配为单独的闭包——即,创建单个对象实例将涉及创建许多函数对象。由于JavaScript对这个的处理非常糟糕,而且它的函数标识的概念也非常糟糕,因此一般来说,没有简单的方法可以优化这些分配
因此,EcmaScript 6选择了当前的模型(尽管目前只有public),TypeScript遗憾地紧随其后(对于public和private)。然后,私有成员仅在TypeScript中提供静态检查,而没有封装保证
FWIW,在这个模型中提供私密信息的正确方式是私密符号(a.k.a.),但不幸的是,这些符号没有进入ES6,并且无论如何都不能用于TypeScript。1)已经解决了2)和3)语言作者的设计决策。TypeScript完全是关于开发人员的体验,因此,如果使用TypeScript,就不会调用任何私有函数,因为编译器会阻止它。原型
方式不需要闭包。很酷,这让事情更清楚了。好的,从其他运行时的角度来看,比如.NET和JVM,我假设您的语句:“每个方法函数都必须编译,内存中的空间必须为新函数保留”对于这些语言来说是正确的?编译的、基于类的语言和解释的、基于原型的语言之间有很大的区别。在JavaScript中,我们通过将方法存储在一个原型对象上来节省内存,具有该原型的所有对象在内存中调用相同的函数,只是使用不同的上下文。使用闭包来模拟隐私意味着您没有利用原型,每个对象都有其各自的方法副本。基于类的语言不使用原型,也没有JavaScript的上下文概念,因此很难进行类比。并非每个实例都需要重新编译每个方法。它只需要分配一个新的闭包,即函数对象——仍然很糟糕,但还没有那么糟糕。@AndreasRossberg有趣,出于某种原因,我觉得函数对象是在每次实例化时编译的。哦,这些问题不适用于C#或Java语言。在它们的对象模型中,您不能像JavaScript那样“提取”方法(即执行这种窃取)。因此,不用分配单独的闭包,您可以将所有闭包环境作为隐藏状态放在新对象本身上(它永远不会与JavaScript中的方法分离)。不幸的是,我又一次在我试图实现的工作中使用了扳手,但我现在知道的更好了,而不是继续下去,在那里我将有100到1000行代码需要重构!唉@AndreasRossberg您能否提供一些见解,说明为什么类系统比合适的原型系统更可取?对Object.create()
的优化似乎可以让继承以一种纯粹基于原型的方式完成,这将更具表现力和灵活性