1盲憎、let的基本用法以及l(fā)et和var的區(qū)別
(1) let與var一樣是用來聲明變量的,與var的區(qū)別是let所聲明的變量胳挎,只在let所在的代碼塊內(nèi)有效
eg1:
{
let a = 10;
var b = 1;
}
eg2:
for(let i=0;i<5;i++){
console.log(i);//輸出1,2,3,4
}
console.log(i);//i is not defined
可以看上面這段代碼饼疙,也就是說let聲明的變量只能在{ }里面獲取,在{}外面是獲取不到慕爬,但是var聲明的變量在{}內(nèi)外都能獲取到
(2) let未先聲明變量就用變量會編譯出錯窑眯,但是var不會,會輸出undefined
另外let不允許在同一作用域下聲明已經(jīng)存在的變量澡罚,即在同一作用域下不可以聲明兩個變量名相同的變量伸但,會直接報錯肾请,但是var不會留搔,可見let比var更加規(guī)范化
console.log(a);//報錯:a is not defined
let a=1;
console.log(a);//undefined
var a=1;
}
(3) let在for循環(huán)內(nèi)作用域問題1
{
for (let i = 0 /* 作用域a */; i < 3; console.log("in for expression", i), i++) {
let i; //這里沒有報錯,就意味著這里跟作用域與上面a的作用域不同
console.log("in for block", i);
}
(4) let在for循環(huán)內(nèi)作用域問題2
1)關(guān)于下面eg1這段代碼為什么輸出結(jié)果是10呢铛铁?因?yàn)閒or里面的i的作用域是整個外部區(qū)域隔显,所以,當(dāng)調(diào)用a6的時候饵逐,其實(shí)運(yùn)行的是console.log(i)括眠,而此時因?yàn)榕芡暄h(huán),i的值是10倍权,所以輸出10
2)關(guān)于eg2輸出結(jié)果是6掷豺,因?yàn)樵贓S標(biāo)準(zhǔn)中捞烟,有一段是關(guān)于CreatePerIterationEnvironment,也就是for語句每次循環(huán)所要建立環(huán)境的步驟当船,里面有提及有關(guān)詞法環(huán)境的相關(guān)步驟(LexicalEnvironment)题画,這與使用let時會有關(guān)。所以德频,如果你使用了let而不是var苍息,let的變量除了作用域是在for區(qū)塊中,而且會為每次循環(huán)執(zhí)行建立新的詞法環(huán)境(LexicalEnvironment)壹置,拷貝所有的變量名稱與值到下個循環(huán)執(zhí)行竞思。
eg1使用var:
var a = [];
for (var i = 0; i < 10; i++) {
// 作用域a
a[i] = function () {
// 作用域b
console.log(i);
};
}
a[6](); // 10
eg2使用let:
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6
eg2:里面的代碼其實(shí)就相當(dāng)于下面這段代碼
var a = [];
{ let k;
for (k = 0; k < 10; k++) {
let i = k; //注意這里,每次循環(huán)都會創(chuàng)建一個新的i變量
a[i] = function () {
console.log(i);
};
}
}
a[6](); // 6
(5) let的暫時性死區(qū):只要塊級作用域內(nèi)存在let命令钞护,它所聲明的變量就“綁定”(binding)這個區(qū)域盖喷,不再受外部的影響,
死區(qū)范圍:在let命令聲明變量tmp之前难咕,都屬于變量tmp的“死區(qū)”传蹈。
下面這段代碼中,聲明了一個全局對象tmp步藕,并在if語句體對tmp進(jìn)行重新賦值惦界。正常來說,該語句是正確的咙冗,但是添加了“l(fā)et tmp”后沾歪,運(yùn)行該程序,就會報錯雾消,顯示“ReferenceError”灾搏。大多數(shù)人往往會忽略到第一個聲明的tmp變量作用域包裹了第二個tmp
例1:
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
例2:
function bar(x = y, y = 2) {//y還沒有定義的時候就使用y
return [x, y];
}
bar(); // 報錯
例3:
// 不報錯
var x = x;
// 報錯
let x = x;
(6)塊級作用域
ES6以前變量的作用域是函數(shù)范圍,有時在函數(shù)內(nèi)局部需要一些臨時變量立润,因?yàn)闆]有塊級作用域狂窑,所以就會將局部代碼封裝到IIEF中,這樣達(dá)到了想要的效果又不引入多余的臨時變量桑腮。而塊作用域引入后泉哈,IIEF當(dāng)然就不必要了!
function f(){
...
swap(var_a,var_b);
(function swap(a,b){
var tmp;
tmp = a;
a = b;
b=tmp;
})(var_a,var_b);
}
如上面的代碼破讨,tmp被封裝在IIFE中丛晦,就不會污染上層函數(shù);而有塊級作用域提陶,就不用封裝成IIEF烫沙,直接放到一個塊級中就好。
{
// 塊級作用域?qū)懛?{
let tmp = ...;
...
}
}
(7)ES6對函數(shù)的改革
{
function add(x,y){return x+y};//這是es5中定義函數(shù)的寫法
var add=(x,y)=>x+y;//es6中我們可以這么搞:
add(1,2);//3,正常運(yùn)行
var fun1=x=>x+1;
fun1(3);//4,當(dāng)參數(shù)為1個的時候 可以再簡單一點(diǎn)隙笆,當(dāng)然復(fù)雜函數(shù)還是需要括號與大括號的
var fun2=(...args)=>{
for(let arg of args){
console.log(arg)
}
};
fun2(1,2,3);//1,2,3配合rest參數(shù)锌蓄,fun2()里面的實(shí)參1,2,3會傳到args被當(dāng)做args的值升筏,...args是es6的擴(kuò)展運(yùn)算符,會把參數(shù)fun2()傳遞的實(shí)參作為一個數(shù)組
}
上面我們提到了擴(kuò)展運(yùn)算符瘸爽,es6擴(kuò)展運(yùn)算符仰冠,也就是... ,作用是將一個數(shù)組轉(zhuǎn)為用逗號分隔的參數(shù)序列蝶糯。
那么問題就來了洋只,我們?yōu)樯兑眠@么奇怪的東東涅,當(dāng)然因?yàn)樗艽蟠筇岣呶覀兊拈_發(fā)效率昼捍。因此识虚,我們可別小看這三個點(diǎn)。下面我們來總結(jié)兩條這三個小點(diǎn)的用途:
(1)復(fù)制數(shù)組:
在es5時代妒茬,要想復(fù)制數(shù)組担锤,最容易想到的是通過for循環(huán)一個一個push,或者來個slice()的乍钻,現(xiàn)在有了擴(kuò)展運(yùn)算符肛循,直接一步就能搞定:
var arr = [1,2,3,4,5];
var copy = [...arr];
(2)將類似數(shù)組的對象轉(zhuǎn)換為真正的數(shù)組
任何類似數(shù)組的對象可以用擴(kuò)展運(yùn)算符轉(zhuǎn)換為真正的數(shù)組。比如:
var newNode= document.querySelectorAll('div');
var array = [...newNode];
Array.isArray(array) //true
var str = 'hello';
var aStr = [...str];
Array.isArray(aStr) //true
(8)關(guān)于es6函數(shù)聲明的行為方式:
1允許在塊級作用域內(nèi)聲明函數(shù)。
2函數(shù)聲明類似于var,即會提升到全局作用域或函數(shù)作用域的頭部伏伯。
3同時,函數(shù)聲明還會提升到所在的塊級作用域的頭部夹孔。
上面的三條規(guī)則只對 ES6 的瀏覽器實(shí)現(xiàn)有效,其他環(huán)境的實(shí)現(xiàn)不用遵守析孽,還是將塊級作用域的函數(shù)聲明當(dāng)作let處理搭伤。
根據(jù)這三條規(guī)則,在瀏覽器的 ES6 環(huán)境中袜瞬,塊級作用域內(nèi)聲明的函數(shù)怜俐,行為類似于var聲明的變量。
考慮到環(huán)境導(dǎo)致的行為差異太大邓尤,應(yīng)該避免在塊級作用域內(nèi)聲明函數(shù)拍鲤。如果確實(shí)需要,也應(yīng)該寫成函數(shù)表達(dá)式裁赠,而不是函數(shù)聲明語句殿漠。如下
// 函數(shù)聲明語句
{
let a = 'secret';
function f() {
return a;
}
}
// 函數(shù)表達(dá)式
{
let a = 'secret';
let f = function () {
return a;
};
}
另外,還有一個需要注意的地方佩捞。ES6 的塊級作用域允許聲明函數(shù)的規(guī)則,只在使用大括號的情況下成立蕾哟,如果沒有使用大括號一忱,就會報錯莲蜘。
// 不報錯
'use strict';
if (true) {
function f() {}
}
// 報錯
'use strict';
if (true)
function f() {}
總結(jié):
1、通過var定義的變量帘营,作用域是整個封閉函數(shù)票渠,是全域的 。通過let定義的變量芬迄,作用域是在塊級或是子塊中问顷。
2、使用 let 聲明的變量禀梳,在聲明前無法使用杜窄,否則將會導(dǎo)致錯誤,并且不允許重復(fù)聲明算途。
3塞耕、如果未在 let 語句中初始化您的變量,則將自動為其分配 JavaScript 值 undefined嘴瓤。
4扫外、變量提升:不論var聲明的變量處于當(dāng)前作用域的第幾行,都會提升到作用域的頭部廓脆。
-var 聲明的變量會被提升到作用域的頂部并初始化為undefined筛谚,而let聲明的變量在作用域的頂部未被初始化
5、暫時性死區(qū)停忿,在代碼塊內(nèi)使用let命令聲明變量之前刻获,該變量都是不可用的,不受外部變量影響瞎嬉;