暫時性死區
暫時性死區也就是變數宣告到宣告完成的區塊,這個區塊是一個封閉的作用域,直到宣告完成。
如果在變數宣告之前使用該變數,那麼該變數是不可用的,也就被稱為暫時性死區。
var
沒有暫時性死區,因為var
存在變數提升let、const
有塊級作用域,沒有變數提升,存在暫時性死區
console.log(a); // 報錯 Cannot access 'a' before initialization
let a = '東方不敗'
console.log(b); // 報錯 Cannot access 'b' before initialization
const b = '東方不敗'
console.log(c); // undefined 因為var存在變數提升
var c = '東方求敗'
ES6
規定,如果程式碼塊中存在let
和const
命令宣告的變數,這個區塊對這些變數從一開始就形成了封閉作用域,直到宣告語句完成,這些變數才能被訪問(獲取或設定),否則會報錯ReferenceError
。這在語法上稱為“暫時性死區”(英temporal dead zone,簡 TDZ),既程式碼塊開始到變數生命語句完成之前的區域。
函式作用域
案例一
一旦設定了引數的預設值,函式進行生命初始化時,引數就會形成一個單獨的作用域,等初始化結束,這個作用域就會消失,這種語法在不設定引數預設值時不會出現。
var x = 1
function f(x,y = x){
console.log(y);
}
f(2) // 2
上面這個例子中,函式引數這裡(x,y = x)
,這個區域就是單獨的作用域,y
預設的x
變數指向第一個引數x
,而不是全域性變數x
,這裡呼叫f函式,向x
傳遞數值2
,y = x
那麼 y = 2
,列印結果為2
案例二
let x2 = 1
function f2(y2 = x2){
let x2 = 2
console.log(y2);
}
f2() // 1
呼叫f2
函式,由於未給f2
函式任何引數,並且 y2 = x2
形成一個單獨的作用域,在這個作用域裡x2
並未定義,所以x2
指向的是外層全域性變數x2
,y2 = x2
也就是y2 = 1
,在這裡,函式內部的x2
並未起到任何作用。
函式執行的時候會先執行引數,再執行函式體。
// 報錯
function f2(y2 = x2){
let x2 = 2
console.log(y2);
}
f2() // 報錯
上面的例子中,如果去掉全域性變數x2
則會報錯,因為變數未宣告,給y2
賦值了一個未宣告的變數,報錯。
var xx = 1
function fxx(xx = xx){
console.log(xx);
}
fxx()
上面這個寫法也會報錯,由於函式的引數存在單獨的作用域,在這個引數作用域內,執行結果為 let xx = xx
,給xx
賦值一個未宣告的變數xx
報錯。(暫時性死區)
如果函式的預設引數是函式,該函式的作用域也要遵循這個規則。
let foo = 'out'
function bar(func = () => foo){
let foo = 'come'
console.log(func());
}
bar() // out
這個例子中,函式的引數是func
預設值是一個匿名函式,返回值為變數foo
,由於函式引數這裡形成一個單獨的作用域,在這個作用域裡面並沒有定義變數foo
,所以foo
會指向外層全域性變數foo
。如果去掉全域性變數foo='out'
報錯,賦值了一個未宣告的變數。
應用
可以利用這個特性寫一個引數預設值錯誤丟擲,如果引數並未傳參則丟擲一個錯誤。
function throwErr(){
throw new Error('引數不得省略')
}
function omits(mustfn = throwErr()){
return mustfn
}
omits(); // 未傳參丟擲錯誤 : 引數不得省略
呼叫omits
函式未傳引數,該函式就會預設呼叫throwErr()
函式並丟擲錯誤。
如果將引數預設值設定為 undefined
則表示該引數是可以省略的。
rest引數
argumentsarguments
可以獲得函式的引數值以及函式資訊(name、length
)等
function au(arr){
console.log('arguments:',arguments);
}
au(2,1,4,3)
可以透過陣列方法對函式引數進行操作,例如排序。arguments
物件不是陣列,而是一個類似陣列的物件,為了使用陣列的方法,必須使用Array.from
先將其轉為陣列。
function au(arr){
// 透過陣列方法對函式引數進行排序
return Array.from(arguments).sort();
}
console.log(au(2,1,4,3)) // [1,2,3,4]
rest引數ES6
提供了rest
引數,語法:(...變數名
),其實就是剩餘運算子,透過rest
引數就可以很容易的對函式引數進行操作,並且rest
的引數是一個真正的陣列。
// resy引數(剩餘運算子)
function residue(...val){
console.log(val); // [1,2,3]
}
residue(1,2,3)
rest
引數(剩餘運算子)只能放到最後一位
,否則報錯
// function residue2(...val,b){} // 剩餘運算子不是最後一位,報錯
function residue3(c,...val){
console.log(c,val); // 1 [2,3,4,5]
}
residue3(1,2,3,4,5)
上面arguments
完成的引數排序,使用rest
可以很輕鬆的做到,並且語義更強,更方便閱讀。
let au2 = (...val) => val.sort()
console.log(au2(2,1,4,3)); // [1, 2, 3, 4]
嚴格模式
ES5
開始,函式內部可以設定為嚴格模式: function s(){ 'use strict'// 嚴格模式 }
// es5嚴格模式
function s(){
'use strict' // 嚴格模式
// 程式碼.....
}
ES6
做了修改,規定只要函式引數使用了預設值、解構賦值、擴充套件運算子,那麼函式內部就不能顯示設定為嚴格模式,否則報錯。
// es6嚴格模式 報錯,因為設定了函式預設值
function s2(a,b = a){
'use strict'
// 程式碼.....
}
// 報錯,使用瞭解構賦值
const s3 = function({a,b}){
'use strict'
}
// 報錯,使用了剩餘運算子
const s4 = (...a) => {
'use strict'
}
es6
這樣設定的原因是,函式內部的嚴格模式應該同樣適用於函式體和函式引數,但是,函式執行的時候會先執行引數,再執行函式體,這樣就有一些不嚴謹的情況,只有函式體中才能知道引數是否應該以嚴格模式執行,但是函式的引數確是先執行,所以es6
修改了函式引數關於嚴格模式的行為。
function s5(val = 070){
'use strict'
return val
}
s5() // 報錯
這一段,函式的預設值是八進位制070
,嚴格模式下不能使用字首0
表示八進位制,所以報錯。
但實際上
是因為函式設定了預設引數的原因,函式先執行引數,再進函式體,由於es6
限制,報錯。
有兩種方法可以規避這種限制
第一種:設定全域性嚴格模式
'use strict'
function s6(val = 100){
console.log(val);
}
s6() // 100
第二種方法:把函式巢狀在一個無引數的立即執行函式里
const s7 = () => {
'use strict'
let a;
return (function(val = 200){ return val })()
}
console.log(s7());
匿名函式的呼叫方法:在上述例子(function(val = 200){return val})()
中,將整個return
的函式用()
套起來,尾部加一個()
呼叫即可,()
在函式中代表呼叫。
案例原始碼:https://gitee.com/wang_fan_w/es6-science-institute
如果覺得這篇文章對你有幫助,歡迎點亮一下star喲