1 作用域和提升
1.1 作用域(Scope)
某个变量名或者函数名,在某个程序片段中是否可见或者可访问,如果是,那么这个程序片段就是这个变量名或者函数名的作用域。比如:
1 var name = "Tom"; 2 function getInlibrary(){ 3 var libraryName = "Xinhua"; 4 console.log(name ); 5 console.log(libraryName ); 6 } 7 8 getInlibrary(); 9 console.log(name ); 10 console.log(libraryName );
输出结果:
Tom
Xinhua
Tom
Uncaught ReferenceError: libraryName is not defined
变量name的作用域是全局,变量libraryName的作用域是函数getInlibrary。使用var来声明变量,作用域要么是全局,要么是函数。
1.2 提升(Hoisting)
先看一段代码:
1 console.log(findAverage(3,5)); 2 function findAverage(a,b){ 3 return (a + b)/2.0; 4 }
控制台将会打印什么结果?undefined?因为打印语句在声明函数之前就执行了。然而答案是,控制台将输入正确的结果:4.
这样先调用函数,后声明函数是可行的!因为JavaScript的提升机制会在代码执行前,将所有函数声明或变量声明提升到它们的作用域顶部,而初始化语句不会被提升!
要注意“它们的作用域”,例如如下代码:
提升前
1 function greeting(){ 2 console.log(hello); 3 var hello = "hello!"; 4 } 5 greeing();
提升后
1 function greeing(){ 2 var hello; 3 console.log(hello); 4 hello = "hello!"; 5 } 6 greeting();
因此,从提升后的代码就可以看出,程序输出结果是undefined了。
2 let和const 代替 var声明变量
2.1 为什么要引入let和const
先看一段代码:
1 function varTest() { 2 var x = 1; 3 if (true) { 4 var x = 2; // same variable! 5 console.log(x); 6 } 7 console.log(x); 8 } 9 varTest();
输入结果:
2
2
上面代码的输出结果,为什么会出现上面的输出结果?看一下提升后的代码:
1 function varTest() { 2 var x; 3 var x = 1; 4 if (true) { 5 x = 2; // 重新赋值 6 console.log(x); // 2 7 } 8 console.log(x); // 2 9 }
从提升后的代码可以看出来,变量x的声明被提升到了函数的顶部,在第3行中被赋值为1,在第5行被重新赋值为了2,因此后面输出都是2.
以上结果显然并不是我们想要的,造成这个结果的原因就是因为ES6之前,JavaScript变量的作用域只有全局和函数两种作用域而已,而缺少块级域,所谓“块”,就是花括号"{}"括起来的部分。
因此ES6引用了let和const,它们与var的区别就在于其作用域延伸了一个:块。将上述代码"var"改为"let"后:
1 function letTest() { 2 let x = 1; //声明了一个变量x, 作用域是letTest函数 3 if (true) { 4 let x = 2; //声明了另一个变量x, 作用域是if块 5 console.log(x); //2 6 } 7 console.log(x); //1 8 } 9 10 letTest();
输出结果:
2
1
以上结果符合了我们的预期!大家仔细体会“另一个变量x”。
2.2 const 、let与var的区别
const、let与var区别是:
let可以重新赋值,不能在同一个作用域重复声明,其声明的变量的作用域为全局、函数或者块。
const不能重新赋值,不能在同一个作用域重复声明,其声明的变量的作用域为全局、函数或者块。
var可以重新赋值,可以在同一个作用域重复声明,其声明的变量的作用域为全局或者函数。
那么,var还有用么?没用了!建议尽可能不要使用var来声明变量了,如果你不需要改变变量值,就用const,如果需要改变变量的值,就用let.