JavaScript暫時性死區(qū)以及函數(shù)作用域
暫時性死區(qū)
暫時性死區(qū)也就是變量聲明到聲明完成的區(qū)塊,這個區(qū)塊是一個封閉的作用域,直到聲明完成。
如果在變量聲明之前使用該變量,那么該變量是不可用的,也就被稱為暫時性死區(qū)。
var
沒有暫時性死區(qū),因為var
存在變量提升let、const
有塊級作用域,沒有變量提升,存在暫時性死區(qū)
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
規(guī)定,如果代碼塊中存在let
和const
命令聲明的變量,這個區(qū)塊對這些變量從一開始就形成了封閉作用域,直到聲明語句完成,這些變量才能被訪問(獲取或設(shè)置),否則會報錯ReferenceError
。這在語法上稱為“暫時性死區(qū)”(英temporal dead zone,簡 TDZ),既代碼塊開始到變量生命語句完成之前的區(qū)域。
函數(shù)作用域
案例一
一旦設(shè)置了參數(shù)的默認(rèn)值,函數(shù)進行生命初始化時,參數(shù)就會形成一個單獨的作用域,等初始化結(jié)束,這個作用域就會消失,這種語法在不設(shè)置參數(shù)默認(rèn)值時不會出現(xiàn)。
var x = 1 function f(x,y = x){ console.log(y); } f(2) // 2
上面這個例子中,函數(shù)參數(shù)這里(x,y = x)
,這個區(qū)域就是單獨的作用域,y
默認(rèn)的x
變量指向第一個參數(shù)x
,而不是全局變量x
,這里調(diào)用f函數(shù),向x
傳遞數(shù)值2
,y = x
那么 y = 2
,打印結(jié)果為2
案例二
let x2 = 1 function f2(y2 = x2){ let x2 = 2 console.log(y2); } f2() // 1
調(diào)用f2
函數(shù),由于未給f2
函數(shù)任何參數(shù),并且 y2 = x2
形成一個單獨的作用域,在這個作用域里x2
并未定義,所以x2
指向的是外層全局變量x2
,y2 = x2
也就是y2 = 1
,在這里,函數(shù)內(nèi)部的x2
并未起到任何作用。
函數(shù)執(zhí)行的時候會先執(zhí)行參數(shù),再執(zhí)行函數(shù)體。
// 報錯 function f2(y2 = x2){ let x2 = 2 console.log(y2); } f2() // 報錯
上面的例子中,如果去掉全局變量x2
則會報錯,因為變量未聲明,給y2
賦值了一個未聲明的變量,報錯。
var xx = 1 function fxx(xx = xx){ console.log(xx); } fxx()
上面這個寫法也會報錯,由于函數(shù)的參數(shù)存在單獨的作用域,在這個參數(shù)作用域內(nèi),執(zhí)行結(jié)果為 let xx = xx
,給xx
賦值一個未聲明的變量xx
報錯。(暫時性死區(qū))
如果函數(shù)的默認(rèn)參數(shù)是函數(shù),該函數(shù)的作用域也要遵循這個規(guī)則。
let foo = 'out' function bar(func = () => foo){ let foo = 'come' console.log(func()); } bar() // out
這個例子中,函數(shù)的參數(shù)是func
默認(rèn)值是一個匿名函數(shù),返回值為變量foo
,由于函數(shù)參數(shù)這里形成一個單獨的作用域,在這個作用域里面并沒有定義變量foo
,所以foo
會指向外層全局變量foo
。如果去掉全局變量foo='out'
報錯,賦值了一個未聲明的變量。
應(yīng)用可以利用這個特性寫一個參數(shù)默認(rèn)值錯誤拋出,如果參數(shù)并未傳參則拋出一個錯誤。
function throwErr(){ throw new Error('參數(shù)不得省略') } function omits(mustfn = throwErr()){ return mustfn } omits(); // 未傳參拋出錯誤 : 參數(shù)不得省略
調(diào)用omits
函數(shù)未傳參數(shù),該函數(shù)就會默認(rèn)調(diào)用throwErr()
函數(shù)并拋出錯誤。
如果將參數(shù)默認(rèn)值設(shè)置為 undefined
則表示該參數(shù)是可以省略的。
rest參數(shù)
arguments
arguments
可以獲得函數(shù)的參數(shù)值以及函數(shù)信息(name、length
)等
function au(arr){ console.log('arguments:',arguments); } au(2,1,4,3)
可以通過數(shù)組方法對函數(shù)參數(shù)進行操作,例如排序。
arguments
對象不是數(shù)組,而是一個類似數(shù)組的對象,為了使用數(shù)組的方法,必須使用Array.from
先將其轉(zhuǎn)為數(shù)組。
function au(arr){ // 通過數(shù)組方法對函數(shù)參數(shù)進行排序 return Array.from(arguments).sort(); } console.log(au(2,1,4,3)) // [1,2,3,4]
rest參數(shù)ES6
提供了rest
參數(shù),語法:(...變量名),其實就是剩余運算符,通過rest
參數(shù)就可以很容易的對函數(shù)參數(shù)進行操作,并且
rest
的參數(shù)是一個真正的數(shù)組。
// resy參數(shù)(剩余運算符) function residue(...val){ console.log(val); // [1,2,3] } residue(1,2,3)
rest
參數(shù)(剩余運算符)只能放到最后一位,否則報錯
// function residue2(...val,b){} // 剩余運算符不是最后一位,報錯 function residue3(c,...val){ console.log(c,val); // 1 [2,3,4,5] } residue3(1,2,3,4,5)
上面arguments
完成的參數(shù)排序,使用rest
可以很輕松的做到,并且語義更強,更方便閱讀。
let au2 = (...val) => val.sort() console.log(au2(2,1,4,3)); // [1, 2, 3, 4]
嚴(yán)格模式
ES5
開始,函數(shù)內(nèi)部可以設(shè)定為嚴(yán)格模式: function s(){ 'use strict'// 嚴(yán)格模式 }
// es5嚴(yán)格模式 function s(){ 'use strict' // 嚴(yán)格模式 // 代碼..... }
ES6
做了修改,規(guī)定只要函數(shù)參數(shù)使用了默認(rèn)值、解構(gòu)賦值、擴展運算符,那么函數(shù)內(nèi)部就不能顯示設(shè)定為嚴(yán)格模式,否則報錯。
// es6嚴(yán)格模式 報錯,因為設(shè)置了函數(shù)默認(rèn)值 function s2(a,b = a){ 'use strict' // 代碼..... }
// 報錯,使用了解構(gòu)賦值 const s3 = function({a,b}){ 'use strict' }
// 報錯,使用了剩余運算符 const s4 = (...a) => { 'use strict' }
es6
這樣設(shè)置的原因是,函數(shù)內(nèi)部的嚴(yán)格模式應(yīng)該同樣適用于函數(shù)體和函數(shù)參數(shù),但是,函數(shù)執(zhí)行的時候會先執(zhí)行參數(shù),再執(zhí)行函數(shù)體,這樣就有一些不嚴(yán)謹(jǐn)?shù)那闆r,只有函數(shù)體中才能知道參數(shù)是否應(yīng)該以嚴(yán)格模式執(zhí)行,但是函數(shù)的參數(shù)確是先執(zhí)行,所以es6
修改了函數(shù)參數(shù)關(guān)于嚴(yán)格模式的行為。
function s5(val = 070){ 'use strict' return val } s5() // 報錯
這一段,函數(shù)的默認(rèn)值是八進制070
,嚴(yán)格模式下不能使用前綴0
表示八進制,所以報錯。
但實際上是因為函數(shù)設(shè)置了默認(rèn)參數(shù)的原因,函數(shù)先執(zhí)行參數(shù),再進函數(shù)體,由于es6
限制,報錯。
有兩種方法可以規(guī)避這種限制
第一種:設(shè)置全局嚴(yán)格模式
'use strict' function s6(val = 100){ console.log(val); } s6() // 100
第二種方法:把函數(shù)嵌套在一個無參數(shù)的立即執(zhí)行函數(shù)里
const s7 = () => { 'use strict' let a; return (function(val = 200){ return val })() } console.log(s7());
匿名函數(shù)的調(diào)用方法:在上述例子(function(val = 200){return val})()
中,將整個return
的函數(shù)用()
套起來,尾部加一個()
調(diào)用即可,()
在函數(shù)中代表調(diào)用。
案例源碼:https://gitee.com/wang_fan_w/es6-science-institute
以上就是JavaScript暫時性死區(qū)以及函數(shù)作用域的詳細內(nèi)容,更多關(guān)于JS暫時性死區(qū)函數(shù)作用域的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaScript基礎(chǔ)之靜態(tài)方法和實例方法分析
這篇文章主要介紹了JavaScript基礎(chǔ)之靜態(tài)方法和實例方法,簡單分析了javascript靜態(tài)方法及實例方法的定義、使用相關(guān)操作技巧與注意事項,需要的朋友可以參考下2018-12-12Javascript日期格式化format函數(shù)的使用方法
這篇文章主要介紹的是javascript時間格式format函數(shù),我們有時候調(diào)用的new Date()不是格式化的時間,可能顯示不是很正常,那么這時候就需要格式化時間,今天這里分享一個javascript的foramt()函數(shù),有需要的可以參考借鑒。2016-08-08