欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JavaScript解析机制之变量提升

程序员文章站 2023-11-21 13:10:28
1、什么是预解析? 在当前作用域下,JS 运行之前,会把带有 var 和 function 关键字的事先声明,并在内存中安排好。(这个过程也可以理解为变量提升)然后再从上到下执行 JS 语句(预解析只会发生在通过 var 定义的变量和 function 上) 2、var 声明的变量 使用 var 声 ......

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

我的博客也会同步至腾讯云+社区,邀请大家一同入驻: