Javascript的预解析
在Javascript当中,存在 预解析 这么一个词儿。解释它之前,这得提及到JS引擎的运行机制:
JS引擎运行javascript分为2步:
1.预解析
2.代码的顺序执行
由此可见,预解析是JS引擎的首个步骤。
预解析:把js里面所有的 var还有function提升到当前作用域的最前面
预解析的组成部分:
1.变量预解析(变量提升):把所有涉及var的变量声明提升到当前的作用域最前面,但不提升赋值操作
2.函数预解析(函数提升) :把所有的函数声明提升到当前作用域的最前面,但不调用函数
为了方便理解,下面举一些例子。
1.变量提升/变量预解析
// 1)变量提升:将var声明的变量提升到js作用域的最前面,但是不提升变量的赋值
console.log(name);
var name = '晓晓';
// 上面的执行顺序:
// var name; // 变量提升
// console.log(name);
// name = '晓晓';
运行结果:
2.函数提升/函数预解析
// 2) 函数提升 :把所有的函数声明提升到当前作用域的最前面,但不调用函数
f1();
function f1() {
console.log("What a nice day!");
}
// 上面的执行顺序:
// function f1() { // 函数声明
// console.log("What a nice day!");
// }
// f1(); // 代码的顺序执行
运行结果:
3.踩坑记录
3.1 坑一知识点:let声明的变量无法进行变量提升,因此必须先声明后使用
// 报错1: name is not defined
console.log(name);
name = '元元';
// 报错2:Cannot access 'name2' before initialization
console.log(name2);
let name2 = '淳淳';
// 正确:先声明/初始化,后调用
let name3 = '元淳';
console.log(name3);
3.2 坑二知识点:var声明的变量被赋值了函数,但是也是属于变量提升
// 报错1: f2 is not a function
f2();
var f2 = function() {
console.log("I'm a music lover.");
}
// 其执行的步骤
// var f2;
// f2();
// f2 = function() {
// console.log("I'm a music lover.");
// }
// 正确:先声明/初始化,后调用
var f2 = function() {
console.log("I'm a music lover.");
}
f2();
3.3 坑三:加入作用域链后,变量提升和函数提升综合使用遇到的问题
// 运行结果: undefined
var num = 10;
f1();
function f1() {
console.log(num);
var num = 20;
}
// 原因:由于作用域链的原因,根据就近原则,控制台打印的num是f1()里面的num。
// 此段代码包含了变量提升和函数提升。关键在于变量提升,导致f1()的该num先被声明,然后再依次执行console语句、num的赋值,因此输出的是undefined
// 解决方法:将num2的初始化语句提前
// 运行结果:20
var num2 = 10;
f2();
function f2() {
var num2 = 20;
console.log(num2);
}
3.4 坑四:当全局变量和局部变量混入JS预解析当中
f3();
console.log('外部:c = ' + c);
console.log('外部:b = ' + b);
console.log('外部:a = ' + a); // 这一步报错,导致下面的'+++'输出无法执行 原因:a不是全局变量
console.log('+++++++++++++++++++++++++++++++');
function f3() {
var a = b = c = 9; // 这部分属于集体声明,相当于var a = 9;b = 9;c = 9; 由于b和c未被定义,因此b和c被当作全局变量,而a被视为局部变量
console.log('--------------------------');
console.log('内部:c = ' + c);
console.log('内部:b = ' + b);
console.log('内部:a = ' + a);
}
运行结果: