本文共 1800 字,大约阅读时间需要 6 分钟。
预编译,顾名思义就是是做些代码文本的替换工作。是整个编译过程的最先做的工作。JavaScript 的圣经 —— MDN 明确地说过, JavaScript 是一个解释型语言,而并非编译型语言,所以预编译对于JS来说是子虚乌有的。但是人们喜欢称之为预编译其实也无伤大雅,关键在于为什么人们会认为JS存在预编译,是JS的哪种行为让人们觉得它产生了预编译呢?让我们来简单聊一聊。
在代码执行前,编译器会进行如下操作:
分词。就是将代码分成原子符号(token)
将token解析翻译成AST(语法生成树)。我的老师在和我讲相关知识点前和我说过这么一个笑话:
面试时我因为这么一个问题挂了var a = 100function foo(){ console.log(a)}foo()
问:为什么输出a的值是100?
答:因为100赋值给了a。 老师说完我头昏脑涨,完全不明白笑点在哪,后来才知道这是在考察“预编译”这方面的知识。 总的来说,“预编译”可以分为创建GO对象(global object) 发生在页面加载完成时
创建AO对象(activation object) 发生在函数执行前一刻具体步骤如下:
全局预编译1. 创建GO对象2. 找变量声明,将变量声明作为GO对象的属性名,并赋值undefined3. 找全局里的函数声明,将函数名作为GO对象的属性名,值赋予函数体局部预编译1. 创建一个AO对象2. 找形参和变量声明,将形参和变量声明作为AO对象的属性名,值为undefined3. 将实参和形参统一4. 在函数体里找函数声明,将函数名作为AO对象的属性名,值赋予函数体
所以那个笑话里的面试问题我们应该这么回答:
首先,编译器创建一个GO对象找到变量声明 var a和函数声明 function foo(){ }将上面两个变量声明作为GO的属性名赋初值GO{ a:undefined foo:function(){ }}然后运行第一行代码 a=100在GO中将100赋值给a再执行第五行代码运行foo函数创建一个AO对象在函数体内找变量声明和形参,(无)再在函数体内找函数声明(无)所以AO{ }完成后运行第三行代码,输出a先在AO对象中寻找a的值,发现不存在,向外部作用域扩展,在GO对象中寻找a,发现a的值为100输出100
当然,笑话里的题过于简单,但是能让我们清晰的了解到这个“预编译”的进行
下面,我们来看一道面试题简化版,练练手:global = 100 function fn() { console.log(global); global = 200 console.log(global); var global = 300 } fn()
它的逻辑和输出结果是多少呢?通过一步步的分析我们可以知道具体的分析应该是这样的:
GO: { global: undefined => 100, fn: function() { } } global = 100 // 没有声明的变量默认为全局变量,也会放到GO中 function fn() { console.log(global); // 输出undefined global = 200 console.log(global); // 输出200 var global = 300 } AO: { global: undefined => 200 => 300 } fn()
文章到这里就结束了,希望大家点个赞哈。如果有错误,欢迎评论区指导
转载地址:http://nkbz.baihongyu.com/