JavaScript解析机制之变量提升
1、什么是预解析?
在当前作用域下,js 运行之前,会把带有 var 和 function 关键字的事先声明,并在内存中安排好。(这个过程也可以理解为变量提升)然后再从上到下执行 js 语句(预解析只会发生在通过 var 定义的变量和 function 上)
2、var 声明的变量
使用 var 声明的变量预解析:告诉解析器知道有这个名字的存在并默认将该变量赋值 undefined ,如下:
console.log(x); //undefined var x = 5;
变量 x 虽然是在 console.log 后面定义的,但使用 var 申明的 x 会提前保存在内存中,并赋值 undefined ,然后再从上往下执行 js 语句 。它的执行顺序类似于下面的结构:
var x; console.log(x); //undefined x = 5;
先声明了 x,x 没有定义所以赋值为 undefined ,输出的结果自然为 undefined,然后再给 x 赋值为 5
需注意的是,如果变量声明没有使用 var,则不存在变量提升。如下:
console.log(x); //error: x is not defined x = 5;
x 没有使用 var 声明,所以报错找不到 x
3、functin 声明的函数
使用 function 声明函数的预解析:先告诉解析器这个函数名的存在,然后在告诉解析器这个函数名的函数体是什么,如下:
console.log(f); function f() { console.log("123"); }
声明函数会把整个函数都提升到最前面 ,所以浏览器中结果会输出整个函数,结果如下:
function f() { console.log("123"); }
如果在一个函数作用域中声明一个变量 ,那么它也会提升到函数作用域的最上面,如下:
var x = 5; function f() { console.log(x); // undefined var x = 2; } f();
以上虽然全局作用域声明了一个变量 x,但函数里也声明了一个变量 x,所以会先查找函数里面是否有变量 x,如果有就不会在全局下查找了。函数里面的变量 x 会被提升到函数作用域的最前面 ,并且赋值为 undefined,所以输出结果为 undefined ,类似于如下结构:
var x = 5; function f() { var x; console.log(x); // undefined x = 2; } f();
函数的参数也可以理解为函数作用域的变量 ,如下:
var x = 5; function f(x) { console.log(x); // undefined } f(); console.log(x); // 5
为函数 f 传递一个形参 x,由于函数在调用时没有传递实参(也就是说变量 x 没有赋值),所以为 undefined 。而在全局下输出 x 自然在全局下查找变量 x ,结果为 5
4、函数优先
变量声明和函数声明都会被提升,如果同一个作用域下声明的两个相同变量或相同函数,后一个会覆盖前一个,如下:
var x = 5; var x = 10; console.log(x); // 10 function f() { console.log("xx"); } function f() { console.log("yy"); } f(); // yy
但有一个需要注意的细节,如果声明的变量与函数名相同 ,那又会怎么覆盖呢?可以看如下例子:
var f = 5; function f() { console.log("xx"); } f(); // error: f is not a function
javascript 中,函数的预解析优先级是要高于变量的预解析的。无论函数在什么位置声明,都优选把整个函数提升到最前面。所以上面的例子中,虽然函数 f 是在变量 f 下面定义的,但是在预解析时先解析函数 f,然后再解析变量 f,后面的变量 f 会把前面的函数 f 覆盖,最后 f 为 5 为数值类型,所以调用 f 时报错,f 不是一个函数。
需要注意的是 ,如果变量 f 定义后没有赋值 ,那么函数 f 就不会被覆盖了,如下:
var f; function f() { console.log("xx"); } f(); // xx
掌握以上知识,我们看下面的例子 :
console.log(x); // function x() {console.log(5);} var x = 2; console.log(x); // 2 function x() { console.log(3); } console.log(x); // 2 var x = 3; console.log(x); // 3 function x() { console.log(5); } console.log(x); // 3 x(); // error: x is not a function
以上例子,两个函数 x 优先提升,所以第二个函数 x 覆盖第一个函数 x,然后两个变量 x 提升,由于变量 x 提升后为 undefined,所以第二个函数没有被覆盖,第一个输出 x 结果为第二个函数 function x(){console.log(5);}
随后 x 被赋值为 2 ,所以第二个输出 x 结果为 2
因为第一个函数 x 已经被提升到前面去了,所以第三个输出 x 结果还是 2
随后为 x 赋值为 3,所以第四,第五输出 x 结果为 3。最后调用 x,x 因为是数值类型,所以会报错 x 不是一个函数
我的博客也会同步至腾讯云+社区,邀请大家一同入驻: