[js高手之路] es6系列教程 - var, let, const详解
1 function show( flag ){ 2 console.log( a ); 3 if( flag ){ 4 var a = 'ghostwu'; 5 return a; 6 } else { 7 console.log( a ); 8 return null; 9 } 10 }
我们从es5的变量提升开始说起, 由于变量提升的原因, 上述程序, 在第2行和第7行都能访问到a的值, 只不过是undefined, 如果你不熟悉javascript这种变量的预解释机制,可能会认为第2行和第7行会报错, 只有flag为true的时候,变量a才声明了, 其实javascript在词法解释的时候,会把上述代码解释成下面的样子:
1 function show( flag ){ 2 var a; 3 console.log( a ); 4 if( flag ){ 5 a = 'ghostwu'; 6 return a; 7 } else { 8 console.log( a ); 9 return null; 10 } 11 }
这种机制,在项目中经常误导程序员,哪怕是资深的前端程序员,不小心也容易入坑, 于是ES6引入了块级作用域来强化对变量生命周期的控制.
什么是块级作用域?
1,函数内部
2,块中( 通常指的是一对花括号之间)
es6中使用新的关键词 let 来定义变量, 为块级作用域,上例,如果改成let声明
1 function show( flag ){ 2 console.log( a ); 3 if( flag ){ 4 let a = 'ghostwu'; 5 return a; 6 }else { 7 console.log( a ); 8 return nul; 9 } 10 }
由于let是块级作用域,不会像var一样 产生变量提升, 所以,第2行和第7行 这个时候报错( a is not defined )
只有在flag为true的情况,a会被定义, 而且访问范围在第3行和第6行的大括号之间, 超出这个范围,就访问不到a, 这就是块级作用域
let都有哪些特性呢?
在同一个作用域下,let不能重复定义两个同名的标识符
在不同的作用域下,可以使用同名的标识符
1 var a = 'ghostwu'; 2 let a = 'ghostwu2';
1 let a = 'ghostwu'; 2 let a = 'ghostwu2';
以上两种方式,都会报重定义错误(Identifier 'a' has already been declared)
1 let a = 10; 2 if( a ){ 3 let a = 100; 4 console.log( a ); //100 5 } 6 console.log( a ); //10
以上这种方式,不会报错,因为是两个不同的作用域
let的经典应用
在4个按钮中,获取当前被点击按钮的索引
1 <script> 2 window.onload = function(){ 3 var aInput = document.querySelectorAll("input"); 4 for( var i = 0; i < aInput.length; i++ ){ 5 aInput[i].onclick = function(){ 6 alert( i ); 7 } 8 } 9 } 10 </script> 11 12 <input type="button" value="按钮1"> 13 <input type="button" value="按钮2"> 14 <input type="button" value="按钮3"> 15 <input type="button" value="按钮4">
如果,我们用上面的方法做, 每个按钮点击之后 i 都是4, 因为4个按钮绑定完事件之后,i的值就变成了4, 下次点击按钮的时候,所有的都是4, 因为 i = 4 是共享的
通常,我们会在每个按钮上,加一个自定义索引 绑定 对应的 i 值,再借助this关键字,就可以如愿以偿,如下:
1 window.onload = function(){ 2 var aInput = document.querySelectorAll("input"); 3 for( var i = 0; i < aInput.length; i++ ){ 4 aInput[i].index = i; 5 aInput[i].onclick = function(){ 6 alert(this.index); 7 } 8 } 9 }
还有另一种方法,就是借用立即表达式+闭包的形式, 如下:
1 window.onload = function(){ 2 var aInput = document.querySelectorAll("input"); 3 for( var i = 0; i < aInput.length; i++ ){ 4 aInput[i].onclick = (function( j ){ 5 return function(){ 6 alert( j ); 7 } 8 })( i ); 9 } 10 }
而采用let关键字, 利用块级作用域的特性,就可以轻松达到目的
1 window.onload = function(){ 2 var aInput = document.querySelectorAll("input"); 3 for( let i = 0; i < aInput.length; i++ ){ 4 aInput[i].onclick = function(){ 5 alert( i ); 6 }; 7 } 8 }
var在全局作用域下,会把属性绑定到window上,从而可能产生覆盖属性的隐患,而let关键字只是会遮蔽它,并不会覆盖window上的同名属性
1 var a = 10; 2 console.log( a ); //10 3 console.log( window.a ); //10 4 console.log( 'a' in window ); //true 5 6 let user = 'ghostwu'; 7 console.log( user ); //ghostwu 8 console.log( window.user ); //undefined 9 console.log( 'b' in window ); //false 10 11 12 /* 13 var RegExp = 'ghostwu'; 14 console.log( RegExp ); //ghostwu 15 console.log( window.RegExp ); //ghostwu 16 console.log( 'RegExp' in window ); //true 17 */ 18 19 let RegExp = 'ghostwu'; 20 console.log( RegExp ); //ghostwu 21 console.log( window.RegExp ); //function RegExp() { [native code] } 22 console.log( 'RegExp' in window ); //true
const关键字是用来定义常量的,它有如下特性:
.块级作用域
.声明的时候,必须赋予初始值
.不能被重新赋值
.如果值是一个对象, 那么对象里面的属性是允许被修改的
.不能跟其他标识符重名
1 const user = 'ghostwu'; 2 console.log( user ); 3 user = 'zhangsan'; //报错, 不能重新赋值
1 const user; //报错,定义的时候 没有给初始值
1 const user = { 2 name : 'ghostwu' 3 }; 4 console.log( user.name ); //ghostwu 5 user.name = 'zhangsan'; //对象的属性允许被修改 6 console.log( user.name ); //zhangsan
1 const user = { 2 name : 'ghostwu' 3 }; 4 user = { //报错,不能重新给user赋值 5 name : 'zhangsan' 6 } 7 console.log( user.name ); //报错
1 var a = 10; 2 const a = 10; // 报错,重定义错误
1 let a = 10; 2 const a = 10; //报错,重定义错误
1 if ( true ) { 2 const a = 10; 3 console.log( a ); //10 4 } 5 console.log( a ); //报错:a is not defined