Javascript 使用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

在我继续之前,我想指出,我已经问了一些关于TypeScript的问题,它的编译器,以及它在其整个生命周期和走向版本1.0的路线图中已经实现了什么和没有实现什么

这个问题涉及在TypeScript中使用
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()
    的优化似乎可以让继承以一种纯粹基于原型的方式完成,这将更具表现力和灵活性