JavaScript预编译
JavaScript运行的三部曲
- 语法分析
- 预编译
- 解释执行
在执行代码前还有两个步骤,即语法分析和预编译。
在预编译之前,系统会对整个代码全部扫描一遍,看看有没有低级的语法错误,如少了括号或引号等等。解释执行就是从上到下依次执行函数代码。这两者很好理解,那么,预编译到底是什么呢?又会对代码执行产生什么样的影响?
让我们一起来了解一下。
在说预编译之前我们先熟悉两个重要概念:
- imply global/暗示全局变量:即任何变量,如果变量未声明就赋值,此变量为全局对象(window)所有。window就是全局的域。
- 任何声明的全局变量,皆为window的属性。所以访问一个全局变量可以console.log(a),也可以console.log(window.a)
接下来我们就具体从函数体内预编译和全局预编译两个方面来解释预编译。
函数体内的预编译
函数体内预编译四部曲 //发生在函数执行的前一刻。
- 创建AO对象,即作用域(执行期上下文)
- 找形参和变量声明,将变量和形参名作为AO的属性名,初始值为undefined
- 将形参和实参相统一
- 在函数体里找变量声明,值赋予函数体
举个栗子,代码如下,我们来看一下这段代码在函数体内预编译的详细过程。
<script>
function fn(a) { //定义一个函数fn()
console.log(a);
var a = 123;
console.log(a);
function a() {} //函数的声明在预编译时已经被识别,所以在调用fn(1)时,忽略这条语句
console.log(a);
var b = function() {}
console.log(b);
function d() {}
}
fn(1);
</script>
- 创建一个AO对象。 AO { }
- 找形参和变量声明,将变量和形参名作为AO的属性名,初始值为undefined。变量名和形参名一致的话只写一个就行。
AO { a : undefined
b : undefined
} - 将实参和形参统一。
AO {a : 1
b : undefined
} - 在函数体里找函数声明,值(函数声明)赋予AO对象。
AO {a : function a() {}
b : undefined
d : function d() {}
}
预编译结束后,才执行fn(1)函数。(执行过程为从上到下依次执行)
执行结果为:function a(){} 、123、123、function (){}.
全局预编译
- 创建一个GO对象,即window对象
- 找变量声明。初始值为undefined。注:全局预编译没有形参和实参
- 找全局里的函数声明,函数声明赋值到全局里的GO
<script>
function b(a) {
console.log(a);
}
b(1);
console.log(a);
var a = 2;
console.log(a);
</script>
- 创建一个GO对象。 GO { }
- 找变量声明 GO {a : undefined}
- 找函数声明 GO {a : undefined
b : function b(a){…} }
从上到下执行整个代码。
执行到 b (1)时,先进行函数体的预编译,然后再执行 b (1)函数。
因此 最后显示的结果为:1 undefined 2