1. 概念
当浏览器加载HTML页面时,会先提供一个供全局JS代码执行的环境(即全局作用域,window或者global),在这个环境中,浏览器默认会把所有带var和function的变量进行提前声明或者定义。
(1) 理解声明和定义;
var num = 12;
// 声明: 告诉浏览器在全局作用域中有一个num变量
// 定义: 给变量进行赋值
// 使用只声明未定义的变量值为undefined;使用未声明未定义的变量直接报错
(2) 对于带var和function关键字的变量在预解释时的操作不一样;
含var关键字的提前声明,含function的提前声明并且定义。
(3) 此时的预解释只发生在当前的全局作用域下,函数内部的变量不发生预解释。执行函数时,函数内部的作用域同样会发生预解释。(关于函数执行时,内部发生的流程,见下一篇《JavaScript函数执行内部原理》)
2.预解释机制的槽点
(1) 由于JavaScript没有块级作用域(es6有),在条件判断语句中的var也会被预解释,会带来如下问题。
if(!("num" in window)){ // "num" in window 此时变为true
var num = 1; // num 被预解释
}
console.log(num); // 输出 undefined
(2) 匿名函数之函数表达式的预解释
fn(); // 报错
var fn = function() {
console.log("hello world");
}
// 只对fn进行预解释,等号后面是一个匿名函数,不参与预解释。
(3) 自执行函数定义和执行一起完成,不进行预解释。
(4) return 下面的代码虽然不再执行,但是会预解释;return后面的都是返回值,不进行预解释。
function fn() {
console.log(num);
return function() {};
var num = 1;
}
fn(); // 结果为undefined
(5) 在预解释时,如果名字已经声明过了,则不会再重新声明,但是会重新赋值。变量名和函数名一样也算冲突。
fn(); // 3
function fn() {console.log(1);} // 预解释时声明+定义
fn(); // 3
var fn = 2; // 预解释时声明,执行时定义
fn(); // 报错
function fn() {console.log(3);} // 预解释时定义
3. 实例
- 例1:
console.log(str); // undefined
var str = "test1"; // 预解释阶段声明全局变量str
console.log(str2); // 报错
str2 = "test2"; // 不会参与预解释,只相当于给window添加了属性str2
console.log(fn1); // 1
function fn1(){
return 1;
}
关于全局变量和window属性的区别,点击这里。
- 例2:
console.log(total); // undefined
var total = 0;
function fn(num1,num2) {
console.log(total); // undefined
var total = num1 + num2;
console.log(total); // 300
}
fn(100,200);
console.log(total); // 0