JavaScript预解析
1.预解析是什么
预解析是指js文件或script里的代码在正式开始执行前,进行的一些解析工作,即在全局中寻找var关键字声明的变量和function关键字声明的函数,将其声明提升到当前作用域的最顶端;代码预解析处理顺序始终从上向下,从左至右。
注意:只有var变量和函数才会发生声明提升。var 变量在声明提升是不会赋值(局部提升,仅提升var 变量)。
预解析时,变量声明在上,函数声明在下
说这么多,不如直接看代码
(1)变量与函数的提升
console.log(a);// undefined
var a = 10;
function f() {
console.log(b);
var b = 20;
}
f();// undefined
正常理解,JS代码是顺序执行,且在console.log(a)输出变量a时,a变量并未声明,此时输出a应报错才对,同理f()函数中输出变量b时,b并未声明,这也应该报错才对,但实际输出undefined并未报错。上面这段代码和下面这段代码等同:
var a;
console.log(a);// undefined
a = 10;
function f() {
var b;
console.log(b);
b = 20;
}
f();// undefined
由此可见,JS的预解析将变量的声明进行了提升,提升到当前作用域的最顶端。
其实JS预解析也会将函数的声明进行提升
f1();// 未报错,输出f函数被调用
function f1() {
console.log("f函数被调用");
}
和下面的代码的等同,
function f1() {
console.log("f函数被调用");
}
f1();// f函数被调用
(2)预解析的提升位置
function f() {
console.log(c);// undefined
var c = 120;
}
console.log(c);// 报错
变量的提升只会在当前的作用域中提升,提前到当前作用域的最上面。函数中的变量只会提升到函数作用域中的的最前面。
函数表达式预解析提升问题:
f();// 报错 f() is not a function
var f = function () {
console.log("匿名函数被调用");
};
//和下面等同
var f;
f();// 报错
f = function () {
console.log("匿名函数被调用");
};
此时会报错f is not a function,是因为函数表达式的写法其实是变量的赋值,而不是一个函数的定义,预解析只会提升var f的声明,而f=function(){…};是变量的赋值不会提升,所以报错。
(3)拓展:
在下面这行代码中,JS从左向右进行了预解析处理,先运算 a.x = {n: 2},再运算 a = {n: 2}。
解析a.x = {n: 2}时,由于a没有x这个属性,所以在堆内存中会开辟一块地址,专门设置x的属性.
解析 a = {n: 2}时,将a重新赋值,覆盖原来的a,在堆中开辟了一块新的地址内存,此时a的地址指向了 {n: 2},然后a与 {n: 1}切断关联。
但是b的地址还是指向了原来的{n: 1},但是由于x的属性没有赋值,所以返回underfined。
2.了解预解析有什么用?
和了解作用域一样,预解析虽不能使我们写出优美的代码,但是能帮助我们在开发的过程中,是我们理清思路,解答我们的一些困惑,对js的执行有更清晰的了解和认识,更轻松地应对面试和开发。