前言
在学习JavaScript时你可能有很多疑惑。例如先打印变量然后声明变量,打印不会报错,有或是先执行函数,然后声明函数,却能正常执行等等。这正是因为Javascript在执行前有一预解析的过程。本浅谈一下预解析。
一、全局环境
Javascript是一种脚本语言,必须要在执行环境下执行。浏览器下Javascript引擎,node的v8引擎。
脚本运行执行前会产生一个全局执行环境,在此环境下变量称为全局变量。声明的函数也会在全局环境中。
var a = 123;
b = 234;
function fn() {
c = 456;
console.log(123);
}
// window就是全局环境对象 在全局环境下声明的变量和函数都会成为window对象的属性
console.log(window.a); // 123
// 隐式的声明
console.log(window.b) // 234
// 在之前的浏览器这里应该是 456 应该是javascript引擎优化了。
console.log(window.c) // undefined
console.log(window.fn); // ƒ fn() { console.log(123); }
二、局部环境
Javascript在ES6之前没有块级作用域的概念. Javascript是函数作用域的。在函数里面声明的变量变量和函数表达式都是局部的。这里可能涉及到了作用域和作用域链的概念。后续待讲解。
三、预解析
在讲解预解析前我们看看一段代码
function fn(a) {
console.log(a);
var a = 123;
console.log(a);
function a() {}
console.log(a);
console.log(b);
var b = function () {};
console.log(b);
console.log(d());
function d() {
console.log("执行d函数...");
}
}
fn(1);
初学Javascript的看到上述代码估计脑子很乱,不清楚每个console.log打印的结果。不急通过预解析就能了解Javascript执行过程了。
1. 生成全局环境
Javascript在运行前一刻会立刻生成一个全局执行环境。生成一个全局执行上下文对象,称为global object. 将fn成为window的一个属性。
// global object 将 GO 下面是伪代码 便于理解
GO: {
fn: function() {}
}
2. 在fn执行前预解析
全局环境生成完毕后,对fn(1)函数进行预解析。预解析基本分为四个步骤
1. 创建AO(Activation object 执行期上下文对象)对象;
AO: {
}
2. 找到形参和变量声明 将变量和形参名作为AO属性名,值为undefined;
形参: 第1行括号里的 a;
变量声明:第4行 a 第15行的b
AO: {
a: undefined
b: undefined
}
3.将实参值和形参统一
实参为1, 会将AO中的a的值改为实参的值
AO: {
a: 1
b: undefined
}
4. 在函数体里面找到函数声明,值赋予函数体。
此时fn中有两个函数声明:第8行和第20行。注意第14行不是函数声明是函数表达式。
AO: {
a: function () {}
b: undefined,
d: function () {}
}
四 执行代码
预解析完后,将逐行执行。在执行时会从执行上下文中对象中获取需要的变量或者函数。还有赋值。
第2行 打印 a() {}
第4行 会将AO中a的值改为123
AO: {
a: 123
b: undefined,
d: function () {}
}
第6行 打印 123
第10行 打印123
第12行 打印 undefined
第14行 将函数体赋值给AO的b
AO: {
a: 123
b: function () {},
d: function () {}
}
第16行 打印 b() {}
第18行 打印执行d函数 其实在此还会产生一个执行上下文。这个就关于闭包的知识了
总结
本文主要讲解了Javascript的预解析的过程。了解了预解析的过程。对于一些变量或者函数的提升就豁然开朗。其实预解析还做了很多复杂的事情。例如this的指向产生、作用域以及作用域链等等。希望对你有所帮助。