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("xx");
}
声明函数会把整个函数都提升到最前面 ,所以浏览器中结果会输出整个函数,结果如下:
function f() {
console.log("xx");
}
如果在一个函数作用域中声明一个变量 ,那么它也会提升到函数作用域的最上面,如下:
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不是一个函数。
需要注意的是 ,如果变量m定义后没有赋值 ,那么函数就不会被覆盖了,如下:
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不是一个函数。