在javascript中,咱们都知道使用var来声明变量。javascript是函数级做用域,函数内能够访问函数外的变量,函数外不能访问函数内的变量。javascript
函数级做用域会致使一些问题就是某些代码块内的变量会在全局范围内有效,这咱们是很是熟悉的:java
1 for (var i = 0; i < 10; i++) { 2 console.log(i); // 0,1,2...,9 3 } 4 console.log(i); //10 5 6 if(true){ 7 var s = 20; 8 } 9 console.log(s); //20
在es6中增长了let(变量)和const(常量)来声明变量,使用的是块级做用域,变量声明只在代码块内有效:es6
1 for (let i = 0; i < 10; i++) { 2 console.log(i); // 0,1,2...,9 3 } 4 console.log(i); //ReferenceError: i is not defined 5 6 if(true){ 7 let s = 20; 8 } 9 console.log(s); //ReferenceError: s is not defined
咱们最常遇到的循环事件绑定i产生的闭包问题,是由于i绑定在全局,每次访问都会取到全局的i的值,因此访问哪一个元素,获得的i都是循环体最后一轮的i。咱们一般的解决办法是借助IIFE。在es6中let定义的i只在循环体内有效,因此每次循环i都是一个新的变量,因而会产生正常的输出:闭包
1 //使用var 2 var arr1 = []; 3 for (var i = 0; i < 10; i++) { 4 arr1[i] = function() { 5 console.log(i); 6 } 7 } 8 9 arr1[5](); //10 10 arr1[8](); //10 11 12 //使用let 13 var arr2 = []; 14 for (let i = 0; i < 10; i++) { 15 arr2[i] = function() { 16 console.log(i); 17 } 18 } 19 20 arr2[5](); //5 21 arr2[8](); //8
变量提高:ide
在es5中var定义的变量存在变量提高,es6中使用的let不存在变量提高:函数
1 console.log(i); 2 var i = 10; //10 3 4 console.log(s); 5 let s = 20; //ReferenceError: can't access lexical declaration `s' before initialization
函数提高:es5
es5中不管if语句是否执行,在if语句内的函数声明都会提高到当前的做用域的顶部而获得执行,es6支持块级做用域,不管是否进入if,内部的函数都不会影响到外部的函数spa
严格模式下函数声明只能在顶层环境和函数体内,不能if或者循环体内进行函数声明code
1 //es5环境 2 function f(){ 3 console.log('outside'); 4 } 5 if(true){ 6 function f(){ 7 console.log('inside'); 8 } 9 } 10 11 f(); //inside 12 13 14 //es6环境 15 function d(){ 16 console.log('outside'); 17 } 18 if(true){ 19 function d(){ 20 console.log('inside'); 21 } 22 } 23 24 d(); //outside
每一个代码块中即同一个做用域中,每一个变量只能使用let定义一次,使用var重复定义会进行覆盖,可是使用let重复定义会报错,不一样代码块能够重复定义:对象
1 let i = 10; 2 3 if(true){ 4 let i = 20; 5 console.log(i); //20 6 let i = 50; //redeclaration of let i 7 console.log(i); 8 }
暂时性死区:
在代码块内,使用let声明变量以前,该变量都不可用,便是变量在使用赋值以前须要先进行声明,每一个代码块会进行变量绑定,以肯定变量属于哪一个环境,在绑定以前进行变量的操做都是非法的:
1 let i = 10; 2 3 if(true){ 4 i = 50; //ReferenceError 5 let i = 20; 6 console.log(i); 7 } 8 //由于在if中声明了i,i绑定了if的环境,因此在if的i声明以前,i都不可用,致使i=50报错
IIFE有一个重要的做用是封装变量,避免变量泄漏到全局,块级做用域的出现,其实就使得IIFE再也不必要了:
1 ~function(){ 2 if(true){ 3 var i = 10; 4 } 5 }() 6 console.log(i); //ReferenceError: i is not defined 7 8 if(true){ 9 let s = 20; 10 } 11 12 console.log(s); //ReferenceError: s is not defined
let和const的区别:
let声明变量,值能够改变。const声明常量,值不可改变(使用const时,常量最好使用大写)
其余的属性let与const一致
1 let a = 10; 2 a = 20; 3 console.log(a); //20 4 5 const B = 30; 6 B = 40; //TypeError: invalid assignment to const `b' 7 console.log(B);
const只保证常量名指向的地址不变,并不能保证地址的数据不变,若是想吧对象冻结数据不可改变,可以使用Object.freeze(obj)
1 const a = {}; 2 a.name = 'gary'; 3 console.log(a.name); //gary 4 5 const b = Object.freeze({}); 6 b.name = 'bob'; 7 console.log(b.name); //undefined
在es5中咱们都知道,在全局定义变量其实就是等于在全局对象设置属性,全局对象属性与全局变量是等价的。全局变量的赋值和全局属性的赋值是同一件事
在es6中修改可这一规定,使用let和const和class声明的全局变量与全局对象不等价
1 var a = 10; 2 console.log(a === window.a); //true 3 4 let b = 20; 5 console.log(b === window.b); //false