JavaScript 预编译:函数声明提升,变量声明提升
2018年06月21日 20:45:17 __Amy 阅读数:282
版权声明:solo https://blog.csdn.net/sansan_7957/article/details/80765102
我们都知道 js 中函数声明会提升,变量声明会提升(赋值不提升)。那为什么会这样呢,这就涉及到 js 预编译。
js 运行三部曲
- 语法解析——检查有无语法错误;
- 预编译;
- 解释运行——将 js 翻译成计算机识别的语言(0 和 1组成的),翻译一行执行一行。
预编译什么时候发生
- 预编译不仅发生在函数体内,还发生在全局;
- 预编译发生在函数或代码执行前一刻。
说预编译之前有两点需要我们记住
-
暗示全局变量(imply golbal):任何变量未经声明(没有用 var 关键字声明)就赋值,此变量就为全局变量所有;
a = 123; // a 是全局变量 var a = b = 123; // b 是全局变量。赋值一定是从右向左的
一切声明的全局变量都是 window 的属性(window 就是全局)。
预编译的步骤
函数:
- 创建 AO( Activation Object ) 对象(执行期上下文);
- 找形参和变量声明,将形参和变量名作为 AO 对象的属性名,值为 undefined(有重复的名称只写一个即可);
- 将形参与实参值统一(用实参的值替换 undefined);
- 在函数体中找函数声明,将函数名添加到 AO 对象的属性中,值为函数体(如属性名重复,则覆盖前面的)。
最后得到一个 AO 对象,代码运行时按照 AO 对象来。例如:
function fn(a) {
console.log(a);
var a = 123;
console.log(a);
function a() {}
console.log(a);
var b = function() {}
console.log(b);
function d() {}
console.log(d);
}
fn(1);
以上代码按照前面的四个步骤:
1、创建 AO 对象
AO = {}
2、找形参和变量声明,将形参和变量名作为 AO 对象的属性名,值为 undefined
AO = {
a: undefined,
b: undefined
}
3、将形参与实参值统一
AO = {
a: 1,
b: undefined
}
4、在函数体中找函数声明,将函数名添加到 AO 对象的属性中,值为函数体。
AO = {
a: function a() {},
b: undefined,
d: function d() {}
}
预编译完成后得到的 AO 对象
预编译完成后运行代码:
function fn(a) {
console.log(a); // function a() {}
var a = 123;
console.log(a); // 123 这时的 AO 对象变成
AO = {
a: 123,
b: undefined,
d: function d() {}
}
function a() {}
console.log(a); // 123
var b = function() {} // 这时的 AO 对象变成
AO = {
a: 123,
b: function() {},
d: function d() {}
}
console.log(b); // function() {}
function d() {}
console.log(d); // function d() {}
}
fn(1);
全局:
- 创建 GO( Global Object ) 对象;
- 找变量声明;
- 找函数声明。
全局变量也是一样的:
console.log(a);
var a = 123;
function a() {}
console.log(a);
第一步:创建 GO( Global Object ) 对象
GO = {}
第二步:找变量声明
GO = {
a: undefined,
}
第三步:找函数声明
GO = {
a: function a() {},
}
预编译完成后得到的 GO 对象
函数运行结果:
console.log(a); // function a() {}
var a = 123;
function a() {}
console.log(a); // 123
再看几个例子
console.log(test);
function test(test) {
console.log(test);
var test = 321;
console.log(test);
function test() {}
}
test(1);
var test = 123;
console.log(test);
按照以上的步骤得到的两个对象分别是:
GO = {
test: function test(test) {
console.log(test);
var test = 321;
console.log(test);
function test() {}
}
}
AO = {
test: function test() {}
}
执行结果:
function test(test) {
console.log(test);
var test = 321;
console.log(test);
function test() {}
}
function test() {}
321
123
global = 100;
function fn4() {
console.log(global);
global = 200;
console.log(global);
var global = 300;
}
fn4();
var global;
两个对象:
GO = {
global: 100
}
AO = {
global: undefined
}
执行结果:
undefined
200
function fn5() {
console.log(b);
if(a) {
var b = 100;
}
c = 234;
console.log(c);
}
var a;
fn5();
a = 10;
console.log(c);
两个对象:
GO = {
a: undefined,
fn5: function fn5() {
console.log(b);
if(a) {
var b = 100;
}
c = 234;
console.log(c);
},
c: undefined // 变量 c 未经声明(没有用 var 关键字声明)就赋值,此变量就为全局变量所有,所以添加到 GO 对象中
}
AO = {
b: undefined
}
对于函数体内变量的值(如此例中的 c),AO对象中有就用AO对象中的值,AO对象中没有就用GO对象中的值
运行结果:
undefined
234
234
总结
综上,所以有我们都知道的:
- 函数声明提升
- 变量声明提升(赋值不提升)