变量提升和函数声明提升
之前一直在看变量提升和函数声明提升的讲解。看了很多但是实际操作中感觉掌握的还是不太好,索性整理一下
1.变量提升
变量提升的作用域是整个函数,var声明的函数会被提升到所在作用域的最顶端。意思就是说函数中的所有地方都是变量提升的范围,但是只会提升到所在作用域的顶端。
function f (){
console.log(y)//undefined
var y = 1;
}
f();
console.log(y);//报错
像在以上的代码中, 在运行后会先显示undefined再显示报错。这是因为一开始在f函数里运行的时候y进行了一次变量提升,但是并没有对y进行赋值操作,所以显示是undefined,而在f函数外再次进行输出时,由于y只在f函数里生效,所以会显示报错。
即一开始进行函数提升的时候是不会对其赋值的,赋值操作要等到后面用等于号进行。相当于是进行了以下操作:
function f (){
var y;
console.log(y)
y = 1;
}
f();
console.log(y);
那如果外部还有一个变量的情况会怎么样呢?
var y = 5;
function f (){
console.log(y)//这里会显示undefined,因为函数里已经有y了,所以会先优先输出函数里的y,但是函数里的y尚未赋值,所以为undefined。
var y = 1;
}
f();
console.log(y);//5,外部变量y赋值5
PS:另外要注意的是JS的函数之中是没有块级作用域的(现在的ES6中已经有块级作用域了)。只有全局执行环境(全局环境)和函数执行环境。
我们可以通过以下代码来加深印象:
var i =5;
function f() {
console.log(i);
if (false) {
var i = 10;
}
}
f(); =>undefined
这是因为在JS中是没有块级作用域的(即if判断语句里的i会变量提升到console.log前,所以输出undefined)。
相应的,我们再看一个类似的代码:
var i =5;
function f() {
console.log(i);
function k() {
var i = 10;
}
}
f(); =>5
结果为5是因为var i = 10; 这串代码是在k这个函数的执行环境里,变量声明提升也就只能到k函数的开头。所以当consolie.log需要一个i输出时,没法取到k函数里的i,只能从全局执行环境里找,即i为5。
也就是说,在代码执行之前编译器会对代码进行编译,并且会在该作用域的开头声明变量(也就是变量提升),而赋值操作是在代码执行时才会执行的。
要注意的是ES6中的let关键字声明的变量不会发生变量提升。
2.函数声明提升
函数的声明提升面对不同的声明方式是有区别的,对于函数声明是会进行函数提升的,而函数表达式是不会对其进行函数提升的。
函数声明
console.log(one()); => 3
function one() {
var i = 1,j = 2;
return i + j;
}
这串代码由于one函数是函数声明而不是函数表达式,所以在编译的时候会跑到console.log前面,因而可以输出三。即以下代码:
function one() {
var i = 1,j = 2;
return i + j;
}
console.log(one()); => 3
函数表达式
console.log(one());=> 报错显示: Uncaught TypeError: one is not a function
var one = function () {
var i = 1, j = 2;
console.log(i + j);
}
这是因为在编译时代码实际为:
var one;
console.log(one());//此时只是将one这个函数名提升了,函数内部并没有提升。
var one = function () {
var i = 1, j = 2;
console.log(i + j);
}
所以总结下来就是编译时遇到函数声明会将整个函数声明提升,而函数表达式只是提升一个函数名,里面的内容并没有提升。
同时要注意的是当同时遇到函数声明和函数表达式时,会优先提升函数声明。
console.log(one()); => 10
var one = function () {
var i = 1, j = 2;
return i + j;
}
function one() {
var i = 5, j = 5;
return i + j;
}
例如以上代码在运行时会输出10,因为函数声明会提示到console.log前面,并输出结果10。