前言预编译,顾名思义,就是做一些代码文本替换的工作。它是整个编译过程的第一个工作。JavaScript圣经-MDN明确指出JavaScript是一种解释型语言,而不是编译型语言,因此JS不存在预编译。但是人们喜欢叫它预编译,其实是无害的。关键是为什么大家认为JS有预编译。JS的什么样的行为让人认为它产生了预编译?让我们简单谈谈。Introduction在代码执行之前,编译器会进行以下操作:1.parsewordsegmentation。就是把代码分割成原子符号(tokens),把token解析翻译成AST(syntaxgenerationtree)。2.当在analyze阶段遇到语句时,该语句会被传递给作用域(scope),创建绑定,分配内存并默认设置变量为undefined或函数体。然后就可以执行代码了。每次在执行过程中遇到赋值或值时,都会从作用域中搜索绑定。这样看,是不是有点“预编译”了?但其实叫它预处理更合适。接下来我们来看一波看起来最像预编译的操作——第三步的干货。《预编译》老师在给我讲相关知识点之前给我讲了这样一个笑话:面试的时候因为这样一个问题挂了。vara=100functionfoo(){console.log(a)}foo()问:为什么输出值为100?答:因为100赋给了a。老师讲完后,我头晕目眩,完全听不懂这个笑话。后来才知道这是在考察“预编译”的知识。一般来说,“预编译”可以分为创建GO对象(全局对象)、在页面加载时创建AO对象(激活对象)、函数执行前创建AO对象(激活对象)。具体步骤如下:全局预编译1、创建GO对象2、找到变量声明,将变量声明作为GO对象的属性名,赋值undefined3。在全局中找到函数声明,将函数名作为GO对象的属性名,赋值给函数体进行局部预编译1.创建一个AO对象2.找到形参和变量声明,并使用形参和变量声明为AO对象的属性名,值为undefined3。统一实参和形参4.在函数体中找到函数声明,将函数名作为AO对象的属性名,值赋给函数体,所以面试题应该在那回答开个玩笑:首先,编译器创建一个GO对象,找到变量声明vara和函数声明functionfoo(){},将以上两个变量声明作为GO对象,将属性名赋给初始值GO{a:undefinedfoo:function(){}},然后运行第一行代码a=100,将100赋值给GO中的a,然后执行第五行代码运行foo函数创建AO对象。在函数体中找到变量声明和形参,(none)然后在函数体中找到函数声明(none),所以AO{}完成后,运行第三行代码,输出a首先查找AO对象中a的值,发现不存在,作用于外域扩展,在GO对象中查找a,发现a的值为100,输出100当然,题中笑话太简单了,却能让我们清楚地了解这次“预编译”的进度接下来,我们来看一道面试题简体版,练练手:global=100functionfn(){console.log(global);global=200console.log(global);varglobal=300}fn()它的逻辑和输出是什么?通过一步步分析我们可以知道,具体分析应该是这样的:GO:{global:undefined=>100,fn:function(){}}global=100//没有声明的变量默认为全局变量,也会放在GOfunctionfn(){console.log(global);//Outputundefinedglobal=200console.log(global);//Output200varglobal=300}AO:{global:undefined=>200=>300}fn()
