從一道詭異的JS面試題說“作用域”與“提升”
“面試造火箭,工作擰螺絲”。面試題目之詭異,常令人匪夷所思。試看一道考察“Hoisting"的面試題目:
一、提升全局變量 var
- var tmp = new Date();
- function f() {
- console.log(tmp);
- if (false) {
- var tmp = "hello";
- }
- }
- f();
JS新手往往會(huì)以為將正常打印出日期,而實(shí)際輸出的確是`undefined`!
- > var tmp = new Date();
- > function f() {
- ... console.log(tmp);
- ... if (false) {
- ..... var tmp = "hello";
- ..... }
- ... }
- > f();
- undefined
這是因?yàn)樵诤瘮?shù)f()的內(nèi)部,var被提升到定義域的頂部,實(shí)際執(zhí)行為:
- var tmp = new Date();
- function f() {
- var tmp;// 提升到這里,將全局的tmp覆蓋了。var默認(rèn)賦值為undefined
- console.log(tmp);
- if (false) {
- var tmp = "hello";
- }
- }
- f();
也就是說var不僅提升,而且將tmp初始化賦值為undefined。
二、如何才能正常輸入日期呢?
解決方案是將global-scope的var替換為block-scope的let:
- var tmp = new Date();
- function f() {
- //var tmp;// 提升到這里,將全局的tmp覆蓋了。var默認(rèn)賦值為undefined
- console.log(tmp);
- if (false) {
- let tmp = "hello";
- }
- }
- f();
- // 2021-04-02T10:52:30.983Z
這是因?yàn)閘et定義的是local-variable.
三、TDZ臨時(shí)DeadZones
更加詭異的案例,來單獨(dú)看let:
- var tmp = new Date();
- function f() {
- console.log(tmp);
- let tmp = "hello";
- }
- f();
你原以為將會(huì)如常打印出時(shí)間,但卻報(bào)錯(cuò)tmp未定義。
- ReferenceError: Cannot access 'tmp' before initialization
這是因?yàn)?tmp 被提升,其實(shí)際執(zhí)行為:
- var tmp = new Date();
- function f() {
- let tmp; // 提升在這里
- console.log(tmp);
- let tmp = "hello";
- }
- f();
然而區(qū)別于var的是,tmp僅僅被提升,卻不會(huì)被自動(dòng)賦值為undefined,因此會(huì)報(bào)錯(cuò)`ReferenceError`.
該問題就是傳說中的TDZ (temporal dead zone)。解決方案也簡單,就是將所有的let或者const等全部都寫到最上面。