JavaScript解析机制之变量提升

﹥>﹥吖頭↗ 提交于 2020-03-31 18:53:26

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 不是一个函数

我的博客也会同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=e1ebv5s48l8j

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!