let 和 var的區別

let和var的區別

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

ES6規定暫時性死區和let、const語句不存在變量提高

不容許重複聲明

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只用來控制循環,可是循環結束後,它並無消失,泄漏成了全局變量

ES6的塊級做用域

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;
    }
}
複製代碼

do表達式

本質上,塊級做用域是一個語句,將多個操做封裝在一塊兒,沒有返回值

{
    let f = f();
    t = t*t+1;
}
複製代碼

上面代碼中,塊級做用域將兩個語句分裝在一塊兒。可是,在塊級做用域之外,沒有辦法獲得t的值,由於塊級做用域不返回值,除非t是全局變量。

do表達式,能夠返回值

let x = do{
   let t = f();
   t * t + 1;
]
複製代碼

上面代碼中,變量x會獲得整個塊級做用域的返回值

const命令

const命令聲明一個只讀的常量,一旦聲明,常亮的值就不能改變

const a = 200;
a     //200

a =600;   //報錯
複製代碼

const聲明的變量不得改變值,這意味着,const一旦聲明變量,就必須當即初始化,不能留到之後賦值

const foo  
//報錯
複製代碼

const的做用域與let相同,只在聲明的塊級做用域內有效

const命令聲明的常量也是不提高,一樣存在暫時性死區,只能在聲明的位置後面使用

const聲明的常量,也與let同樣不可重複聲明

var message = 'hello';
let age = 25;

//如下兩行都會報錯
const message = 'goodbye';
const age = 30;
複製代碼

常量foo儲存的是一個地址,這個地址指向一個對象,不可變的是這個地址,即不能把foo指向另外一個地址,但對象自己是可變的,因此依然能夠爲其添加新屬性

const foo();
//爲foo添加一個屬性,能夠添加
foo.prop = 123;
foo.prop   //123

//將foo指向另外一個對象,就會報錯
foo ={]     //報錯
複製代碼

下面是另外一個例子

const a = [];
a.push('hello')   //可執行
a.length = 0;     //可執行
a = ['dave']      //報錯
複製代碼

上面代碼中,常量a是一個數組,這個數組自己是可寫的,可是若是將另外一個數組賦值給a,就會報錯

若是真的想將對象凍結,應該使用 Object.freeze方法

const foo = Object.freeze({});
//常規模式下,下面一行不起做用
//嚴格模式下,改行會報錯
foo.prop = 123;





























複製代碼
相關文章
相關標籤/搜索