我今天被问到,无法给出正确的答案。
Typescript 转译为 JS。然后是摇树,“更少”(可选)以及在进行部署过程中的其他内容。但是这样的事情(afaik)与“编译”没有任何关系。一切都被捆绑并进行了高度优化,但实际上并没有编译,对吧?
甚至还有一个“提前”编译器,它确实做了一个引人注目的工作。我想念什么?
Javascript 本身仍然被解释,对吧?
您假设编译意味着获取源代码并生成机器代码、低级代码等。但编译实际上只是意味着获取一个源代码并将其转换为另一个源代码。所以说使用 Typescript 并生成 JavaScript 是一种编译形式似乎是合理的。它与(例如)c# 在编译成 IL 语言时所做的并没有什么不同。
也就是说,我会说一个更好的词是Transpiling。我建议将 Typescript 编译器更好地描述为 Transpiler。
区别是微妙的,转译器可以被认为是一种编译器。但是(纯)编译语言(通常)将高级语言转换为低级语言(更接近机器代码),例如 C# 示例。转译器将高级语言转换为类似级别(抽象)语言(也是高级)。*
编译代码的结果通常不是您自己编写的语言。转译器的结果是另一种高级语言。理论上,您可以编写 IL(作为示例),但它实际上是设计为由编译器生成的,并且没有工具或支持这样做,您只能通过编译 C#/vb.net 来生成 IL。而 Javascript 本身就是一种可用(和使用)的编程语言。
*很多警告,因为这些词的定义及其用法非常模糊
您似乎在一个问题中提出三个问题:
编译器和转译器有什么区别?
Angular 和 TypeScript 是否实现编译器或转译器?
是否有单独的 Angular 编译器?它编译什么?
编译器和转译器有什么区别?
@JörgWMittag provided a very good answer 这个问题。
Angular 和 TypeScript 是否实现编译器或转译器?
TS 和 Angular 都实现了 real 编译器。它们遵循与生成汇编代码的 C/C++ 编译器相同的词法分析、解析、语义分析和代码生成阶段(可能用于优化除外)。您可以看到类/文件夹在 Angular 和 TS 中都被命名为“compiler”。
角度编译器与 TypeScript 编译器并不真正相关。这些是非常不同的编译器。
是否有单独的 Angular 编译器?它编译什么?
Angular 有两个编译器:
查看编译器
模块编译器
视图编译器的工作是将您为组件模板指定的模板转换为组件的内部表示,该组件是视图工厂,然后用于实例化视图实例。
除了转换模板之外,视图编译器还以 @HostBinding
、@ViewChild
等装饰器的形式编译各种元数据信息。
假设您像这样定义一个组件及其模板:
@Component({
selector: 'a-comp',
template: '<span>A Component</span>'
})
class AComponent {}
使用这些数据,编译器生成以下稍微简化的组件工厂:
function View_AComponent {
return jit_viewDef1(0,[
elementDef2(0,null,null,1,'span',...),
jit_textDef3(null,['My name is ',...])
]
它描述了组件视图的结构,并在实例化组件时使用。第一个节点是元素定义,第二个节点是文本定义。通过参数列表可以看到每个节点在被实例化时都得到了它所需要的信息。解析所有必需的依赖项并在运行时提供它们是编译器的工作。
我强烈推荐阅读这些文章:
这是您需要了解的有关 Angular 中动态组件的知识
这就是为什么在 Angular 中找不到组件的原因
另请参阅 What is the difference between Angular AOT and JIT compiler. 的答案
模块编译器的工作是创建一个模块工厂,它基本上包含提供者的合并定义。
有关更多信息,请阅读:
避免与 Angular 中的模块的常见混淆
Typescript 转换为 JS。然后是摇树,“更少”(可选)以及在进行部署过程中的其他内容。但是这样的事情(afaik)与“编译”没有任何关系。一切都被捆绑并进行了高度优化,但实际上并没有编译,对吧?
编译是指将用语言 A 编写的程序转换为用语言 B 编写的语义等价程序,以便根据语言 B 的规则评估编译的程序(例如用 B 的解释器解释它)产生相同的结果并具有与根据语言 A 的规则评估原始程序相同的副作用(例如用 A 的解释器解释它)。
编译只是意味着将程序从语言 A 翻译成语言 B。这就是它的全部含义。 (另请注意,A 和 B 完全有可能是同一种语言。)
在某些情况下,我们对某些类型的编译器有更专业的名称,这取决于 A 和 B 是什么,以及编译器的作用:
如果 A 被认为是汇编语言而 B 被认为是机器语言,那么我们称它为汇编程序,
如果 A 被认为是机器语言而 B 被认为是汇编语言,那么我们称它为反汇编程序,
如果 A 被认为比 B 低级,那么我们称它为反编译器,
如果 A 和 B 是同一种语言,并且生成的程序在某种程度上更快或更轻,那么我们称它为优化器,
如果 A 和 B 是相同的语言,并且生成的程序更小,那么我们称它为 minifier,
如果 A 和 B 是相同的语言,并且生成的程序可读性较差,那么我们称它为混淆器,
如果 A 和 B 被认为处于大致相同的抽象级别,那么我们称其为转译器,并且
如果 A 和 B 被认为处于大致相同的抽象级别,并且生成的程序保留了格式、注释和程序员意图,以便可以以与原始程序相同的方式维护生成的程序,那么我们调用它是一种重新设计的工具。
另外,请注意,较旧的来源可能使用术语“翻译”和“翻译器”而不是“编译”和“编译器”。例如,C 谈到“翻译单元”。
您可能还会偶然发现“语言处理器”一词。这可能意味着编译器、解释器或编译器和解释器,具体取决于定义。
Javascript 本身仍然被解释,对吧?
JavaScript 是一种语言。语言是一组逻辑规则和限制。语言不被解释或编译。语言就是。
编译和解释是编译器或解释器的特征(呃!)。每种语言都可以用编译器实现,每种语言都可以用解释器实现。许多语言都有编译器和解释器。许多现代高性能执行引擎都具有至少一个编译器和至少一个解释器。
这两个术语属于不同的抽象层。如果英语是一种类型化语言,“interpreted-language”将是类型错误。
另请注意,某些语言既没有解释器也没有编译器。有些语言根本没有实现。不过,它们是语言,你可以用它们编写程序。你只是无法运行它们。
另外,请注意,一切都在某个时刻被解释:如果你想执行某些东西,你必须解释它。编译只是将代码从一种语言翻译成另一种语言。它不运行它。解释运行它。 (有时,当解释器在硬件中实现时,我们称其为“CPU”,但它仍然是解释器。)
恰当的例子:当前存在的每一个主流 JavaScript 实现都有一个编译器。
V8 最初是一个纯编译器:它将 JavaScript 直接编译为经过适度优化的本机机器代码。后来,添加了第二个编译器。现在,有两个编译器:一个轻量级编译器,它生成适度优化的代码,但编译器本身非常快并且使用很少的 RAM。该编译器还将分析代码注入到已编译的代码中。第二个编译器是一个更重量级、更慢、更昂贵的编译器,然而,它产生的代码更紧凑、更快。它还使用第一个编译器注入的分析代码的结果来做出动态优化决策。此外,使用第二个编译器重新编译哪些代码是基于该分析信息做出的。请注意,任何时候都不会涉及口译员。 V8 从不解释,它总是编译。它甚至不包含解释器。 (实际上,我相信现在确实如此,我正在描述前两次迭代。)
SpiderMonkey 将 JavaScript 编译为 SpiderMonkey 字节码,然后对其进行解释。解释器还分析代码,然后最常执行的代码由编译器编译为本机机器代码。因此,SpiderMonkey 包含两个编译器:一个从 JavaScript 到 SpiderMonkey 字节码,另一个从 SpiderMonkey 字节码到本地机器码。
几乎所有 JavaScript 执行引擎(V8 除外)都遵循这种将 JavaScript 编译为字节码的 AOT 编译器模型,以及在解释和编译该字节码之间切换的混合模式引擎。
你在评论中写道:
我真的在想机器代码涉及到某个地方。
“机器码”到底是什么意思?
什么是一个人的机器语言是另一个人的中间语言,反之亦然?例如,有些 CPU 可以本地执行 JVM 字节码,在这样的 CPU 上,JVM 字节码是本地机器码。并且有 x86 机器码的解释器,当您运行这些 x86 机器码时,它们会被解释为字节码。
有一个用 Java 编写的称为 JPC 的 x86 解释器。如果我在本机 JVM CPU 上运行的 JPC 上运行 x86 机器代码……哪个是字节码,哪个是本机代码?如果我将 x86 机器代码编译为 JavaScript(是的,有可以做到这一点的工具)并在我的手机(具有 ARM CPU)上的浏览器中运行它,哪个是字节码,哪个是本机机器代码?如果我正在编译的程序是一个 SPARC 仿真器,我用它来运行 SPARC 代码怎么办?
请注意,每种语言都衍生出一个抽象机器,并且是该机器的机器语言。因此,每种语言(包括非常高级的语言)都是本机机器代码。此外,您可以为每种语言编写解释器。因此,每种语言(包括 x86 机器代码)都不是本地语言。
让您编写的代码在浏览器上运行涉及两件事:
1) 将 Typescript 转译成 JavaScript。这是一个已解决的问题。我认为他们只是使用 webpack。
2)将角度抽象编译成JavaScript。我的意思是组件、管道、指令、模板等。这就是 Angular 核心团队的工作。
如果您真的对第二部分感兴趣,那就是角度编译器 watch compiler author Tobias Bosch explain the Angular Compiler at AngularConnect 2016。
我认为在转译和编译之间存在一些混淆。这有点无关紧要,是个人品味的问题,它们都只是代码表示之间的转换。但我个人使用的 definition 是 transpilation 是在两种不同语言之间的类似抽象级别(例如 typescript 到 javascript),而 compilation 需要降低级别的抽象。我认为从模板、组件、管道、指令等到只是 javascript 是抽象阶梯的下一个步骤,这就是它被称为编译器的原因。
角度编译器
从 Angular 4 到 5 最重要的变化之一是编译器被重写得更快、更彻底。过去,Angular 应用程序使用我们所谓的即时 (JIT) 编译,其中应用程序在运行之前在浏览器中编译。 Angular 5 中的编译器更新推进了向 AOT 的迁移,这使得应用程序运行得更快,因为它在运行应用程序时执行的编译更少。自 Angular CLI 1.5 版本以来,任何生产版本都默认启用 AOT。
假设我们要构建一个用于部署的应用程序并运行以下命令:
ng build --prod
发生了一些事情:生产版本、缩小、捆绑资产、文件名哈希、摇树、AOT ...(我们可以使用标志启用/禁用它,例如 aot=false)。简而言之,prod 标志通过使用 ngc(Angular 编译器)进行 AOT 编译来创建应用程序的优化包,以创建为浏览器准备的优化代码(是的,它预编译模板)。
打字稿编译器
TypeScript 编译器 tsc 负责编译 TypeScript 文件。编译器负责实现 TypeScript 功能,例如静态类型,结果是纯 JavaScript,其中 TypeScript 关键字和表达式已被删除。
TypeScript 编译器有两个主要特性:它是一个转译器和一个类型检查器。编译器将 TypeScript 转换为 JavaScript。它对您的源代码进行以下转换:
删除所有类型注释。
为旧版本的 JavaScript 编译新的 JavaScript 功能。
编译非标准 JavaScript 的 TypeScript 功能。
调用它时,编译器会搜索在 tsconfig.json 中加载的配置(可以在 here 中找到所有编译器选项的详细列表以及默认值)。
在大多数方面,TypeScript 编译器的工作方式与任何编译器一样。但是有一个区别可以让粗心大意的人发现:默认情况下,即使遇到错误,编译器也会继续发出 JavaScript 代码。幸运的是,可以通过在 tsconfig.json 文件中将 noEmitOnError
配置设置设置为 true 来禁用此行为。
注意:tsc 和 ngc 有不同的目的,并不是要选择一个而不是另一个。 This answer might be of interest。
这个答案是根据以下书籍的内容制作的
克洛,M.(2018)。 “Angular 5 项目:学习使用 70 多个项目构建单页 Web 应用程序”。
Dewey, B.、Grossnicklaus, K.、Japikse, P. (2017)。 “使用 Visual Studio 2017 构建 Web 应用程序:使用 .NET Core 和现代 JavaScript 框架”。
弗里曼,A.(2019)。 “基本 TypeScript:从初学者到专业人士”。
Ghiya, P. (2018)。 “TypeScript 微服务”。
Iskandar, A., Chivukulu, S. (2019)。 “使用 Angular 和 Bootstrap 进行 Web 开发 - 第三版”。
Hennessy, K., Arora, C. (2018)。 “示例 Angular 6”。
Jansen, R., Wolf, I., Vane, V. (2016)。 “TypeScript:现代 JavaScript 开发”。
穆罕默德,Z.(2019)。 “角项目”。
Seshadri, S. (2018)。 “角度:启动并运行”。
威尔肯,J. (2018)。 “角在行动”。