var = [];
for(var i = 0;i<10;i++){
a[i]= function(){
console.log(i);
}
}
arr[6](); //10
複製代碼
上面代碼中,變量i是var申明的,在全局範圍內都有效,因此全局只有一個變量i,每一次循環,變量的值都會發生改變,而循環內被賦值給數組a的function在運行時,會經過閉包讀到這同一個變量i,致使最後一輪輸出最後一輪i的值,就是10javascript
而若是使用let,聲明的變量僅在塊級做用域內有效,最後輸出的是6java
var = [];
for(let i = 0;i<10;i++){
a[i]= function(){
console.log(i);
}
}
arr[6](); //6
複製代碼
上面代碼中,變量i是let聲明的,當前的i只在本輪循環中有效,因此每一次循環中的i都是一個新的變量,因此最後輸出的是6,。 另外,for循環還有一個特別之處,就是循環語句部分是一個父做用域,而循環內部是一個單獨子做用域。數組
for(let i = 0;i<3;i++){
let i ='abc';
console.log(i)
}
//abc
//abc
//abc
複製代碼
var 命令會發生「變量提高」現象,即變量能夠在聲明以前使用,值爲underfined。爲了糾正這種現象,let命令改變了語法行爲,他所聲明的變量必定要在聲明後使用,不然報錯bash
//var狀況
console.log(foo); //輸出underfined
var foo;
//let狀況
console.log(bar); //報錯
let bar ;
複製代碼
只要塊級做用域內存在let命令,他所聲明的變量就綁定(binding)這個區域,不在受外部的影響。閉包
var tmp = 123;
if(true){
tmp = 'abc'; //報錯
let tmp;
}
複製代碼
上面代碼中,存在全局變量tmp,可是塊級做用域內let又聲明瞭一個局部變量tmp,致使後者綁定這個塊級做用域,因此在let聲明變量前,對tmp賦值會報錯。ide
ES6明確規定,若是區塊中存在let和const變量,從一開始就造成了封閉做用域,凡是在聲明以前使用這些變量,就會報錯。函數
總之,在代碼塊內,使用let命令聲明變量以前,該變量都是不可用的。這在語法上叫作「暫時性死區」ui
let不容許在相同做用域內重複聲明同一個變量spa
//報錯
function(){
let a =10;
var a =1;
}
//報錯
function(){
let a = 10;
let a = 1;
}
複製代碼
所以,不能在函數內部從新聲明參數code
function func(arg){
let arg ; //報錯
]
function func(arg){
{
let arg; //不報錯
}
}
複製代碼
ES5只有全局做用域和函數做用域,沒有塊級做用域
var tmp = new Data();
function f(){
console.log(tmp);
if(false){
var tmp = 'hello world'
}
}
f(); //undefined
複製代碼
上面代碼的意思是,if代碼塊的外部使用外層的tmp變量,內層使用內層的tmp變量。可是,函數f執行後,輸出結果爲undefined,緣由在於變量提高,致使內層的tmp變量覆蓋了外層的tmp變量
var s = 'hello';
for(var i = 0;i<s.length;i++){
console.log(s[i]);
}
console.log(i); //5
複製代碼
上面代碼中,變量i只用來控制循環,可是循環結束後,它並無消失,泄漏成了全局變量
let其實是爲javascript新增了塊級做用域
function f1(){
let n = 5;
if(true){
let n = 10
}
console.loga(n) //5
}
複製代碼
上面的函數有兩個代碼塊,都聲明瞭變量n,運行後輸出5,。這代表外層代碼塊不受內層代碼塊的影響。若是使用var定義變量n,最後輸出值就是10. ES6容許塊級做用域的任意嵌套
{{{{let insane = 'hello world'}}}}
複製代碼
上面代碼使用了四層的塊級做用域。外層做用域沒法讀取內層做用域的變量
{{{
{let insane = 'hello world'}
console.log(insane); //報錯
}}}
複製代碼
內層做用域能夠定義外層做用域的同名變量
{{{
let insane = 'hello world'
{let insane = 'hello world'}
}}}
複製代碼
塊級做用域的出現,實際上使得得到普遍應用的當即執行函數再也不必要了
function f(){
console.log('i am outside')
}
(function(){
if(false){
functon f(){
console.log('i an inside')
}
}
f();
}())
複製代碼
上面代碼中,會獲得「i am inside」,ES6就徹底不同了,會獲得「i am outside」.由於塊級做用域內聲明的函數相似let,對做用域以外沒有影響。
//函數聲明語句
{
let a = 'secret';
function f(){
return a;
}
}
//函數表達式
{
let a = 'secret';
let f = function(){
return a;
}
}
複製代碼
本質上,塊級做用域是一個語句,將多個操做封裝在一塊兒,沒有返回值
{
let f = f();
t = t*t+1;
}
複製代碼
上面代碼中,塊級做用域將兩個語句分裝在一塊兒。可是,在塊級做用域之外,沒有辦法獲得t的值,由於塊級做用域不返回值,除非t是全局變量。
let x = do{
let t = f();
t * t + 1;
]
複製代碼
上面代碼中,變量x會獲得整個塊級做用域的返回值
const a = 200;
a //200
a =600; //報錯
複製代碼
const foo
//報錯
複製代碼
var message = 'hello';
let age = 25;
//如下兩行都會報錯
const message = 'goodbye';
const age = 30;
複製代碼
const foo();
//爲foo添加一個屬性,能夠添加
foo.prop = 123;
foo.prop //123
//將foo指向另外一個對象,就會報錯
foo ={] //報錯
複製代碼
下面是另外一個例子
const a = [];
a.push('hello') //可執行
a.length = 0; //可執行
a = ['dave'] //報錯
複製代碼
上面代碼中,常量a是一個數組,這個數組自己是可寫的,可是若是將另外一個數組賦值給a,就會報錯
const foo = Object.freeze({});
//常規模式下,下面一行不起做用
//嚴格模式下,改行會報錯
foo.prop = 123;
複製代碼