JavaScript中標識符提升問題
JS 存在變量提升,這個的設計其實是低劣的,或者是語言實現(xiàn)時的一個副作用。它允許變量不聲明就可以訪問,或聲明在后使用在前。新手對于此則很迷惑,甚至許多使用JS多年老手也比較迷惑。但在 ES6 加入 let/const 后,變量Hoisting 就不存在了。
一、 變量未聲明,直接使用
function test() {
alert(notDefined);
}
test(); // ?
報錯是自然的

二. 變量聲明在末尾
function test() {
alert(declaredButNotAssigned); // undefined
var declaredButNotAssigned;
}
test();
輸出 undefined, 結果比上例有所改善,沒有報錯,代碼可以運行,但變量值可能不是程序員所期望的。
三、 變量聲明在末尾,同時給變量賦值
function test() {
alert(declaredAndAssigned); // undefined
var declaredAndAssigned = 1;
}
test();
結果和 二 相同, 很明顯,并不會因為賦值了就輸出 1。
二、三 都發(fā)生了變量提升(Hoisting),簡單定義
變量提升: 在指定作用域里,從代碼順序上看是變量先使用后聲明,但運行時變量的 “可訪問性” 提升到當前作用域的頂部,其值為 undefined ,沒有 “可用性”。
這里強調(diào) “代碼順序” 和 “運行順序”,是因為多數(shù)時候我們寫的代碼都是順序執(zhí)行的,即 “代碼順序” 和 “運行順序” 是一致的。這也符合人的大腦的思維過程。比如有過 C語言 經(jīng)驗的程序員
#include <stdio.h>
int main() {
int x = 1;
printf("%d, ", x); // 1
}
兩句代碼,先聲明整數(shù)型 x, 再輸出。代碼順序和運行順序是一致的,即正常運行。
如果順序反過來
#include <stdio.h>
int main() {
printf("%d, ", x); // error
int x = 1;
}
此時,編譯都不能通過了。但JS里可以反過來寫,見二、三。
因此,有類 C語言 經(jīng)驗的程序員,都很清楚變量需要 先聲明后使用,不然會報錯。而到了JS里,有 變量提升 現(xiàn)象,可以 先使用后聲明,C 的經(jīng)驗用到 JS 里迷惑便出現(xiàn)了。
四、 函數(shù)表達式也存在變量提升
function test() {
alert(func); // undefined
var func = function() {};
}
test();
但如果想使用這個 func,則無可能
function test() {
alert(func); // undefined
func(); // 報異常
var func = function() {};
}
test();
結果func 是 undefined,調(diào)用 func 則會報異常。 在上面的定義中提到了 可訪問性 和 可用性 對應如下語句。
可訪問性:alert(func),輸出 undefined,可以運行,可以訪問 func。
可用性: func(), 報異常,不能正常調(diào)用 func,表示無可用性。
二、三、四 都是使用 var 聲明的變量,JS 里函數(shù)聲明也會存在提升,只是這個 “變量” 比較特殊,它是一個 function 類型(可以作為函數(shù)、方法或構造器)。它的名字(標識符)也會提升到當前作用域的頂部。
五、函數(shù)聲明的名也會提升到當前作用域頂部
function test() {
alert(f1); // function
f1(); // "called"
function f1() {
alert('called');
}
}
test();
我們看到,聲明 f1 在代碼最末,f1 使用在前,alert(f1) 和 f1() 都正常執(zhí)行,表示 可訪問性 和 可用性 都有了。
前面說了,變量提升(Hoisting)沒什么用,屬于語言的低劣設計,好的習慣還是 “先聲明后使用”。這個特性也會出現(xiàn)在不少大公司面試題里
題1:
// 寫出以下代碼的運行結果
var a = 1;
function fn() {
if (!a) {
var a = 2;
}
alert(a); // ?
}
fn();
題2:
// 寫出以下代碼的運行結果
var a = 1;
function fn() {
a = 2;
return;
function a() {}
}
fn();
alert(a); // ?
但這一切隨著 ES6 的 let/const 到來結束了,ES里除全局變量外,其它都使用 let/const,var 替換成 let 后變量提升就不復存在了。
function test() {
alert(declaredButNotAssigned1); // 報異常
alert(declaredButNotAssigned2); // 報異常
alert(func); // 報異常
let declaredButNotAssigned1;
let declaredButNotAssigned2 = true;
let func = function() {};
}
test();
這強制程序員養(yǎng)成好的習慣,變量需要“先聲明再使用”,否則報錯。
以下摘自MDN的關于let不在發(fā)生變量提升的描述
In ECMAScript 6, let does not hoist the variable to the top of the block. If you reference a variable in a block before the let declaration for that variable is encountered, this results in a ReferenceError, because the variable is in a "temporal dead zone" from the start of the block until the declaration is processed.
用 let 聲明變量后,typeof 也不再安全
if (condition) {
alert(typeof num); // Error!
let num = 100;
}
以前可以用 typeof == 'undefined',來判斷是否引入了某lib,比如jQuery
// 判斷jQuery是否引入了
if (typeof $ !== 'undefined') {
// do something
}...
jQuery沒有引入,$ 沒有聲明,這句也不會報錯而影響到下面的代碼執(zhí)行,但如果是 let 聲明的就會報錯了。
以上所述就是本文的全部內(nèi)容了,希望大家能夠喜歡。
相關文章
基于BootStrap的Metronic框架實現(xiàn)頁面鏈接收藏夾功能按鈕移動收藏記錄(使用Sortable進行拖動排序)
這篇文章主要介紹了基于BootStrap的Metronic框架實現(xiàn)頁面鏈接收藏夾功能按鈕移動收藏記錄(使用Sortable進行拖動排序)的相關資料,非常不錯,需要的朋友可以參考下2016-08-08
JavaScript實現(xiàn)同一頁面內(nèi)兩個表單互相傳值的方法
這篇文章主要介紹了JavaScript實現(xiàn)同一頁面內(nèi)兩個表單互相傳值的方法,涉及javascript鼠標事件及頁面元素賦值操作的相關技巧,需要的朋友可以參考下2015-08-08
淺談webpack devtool里的7種SourceMap模式
這篇文章主要介紹了淺談webpack devtool里的7種SourceMap模式,主要介紹了這7種模式的使用和打包編譯后的結果的不同,非常具有實用價值,有興趣的可以了解一下2019-01-01
js點擊返回跳轉(zhuǎn)到指定頁面實現(xiàn)過程
這篇文章主要為大家詳細介紹了js點擊返回跳轉(zhuǎn)到指定頁面實現(xiàn)過程,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-04-04
html的DOM中document對象anchors集合用法實例
這篇文章主要介紹了html的DOM中document對象anchors集合用法,實例分析了anchors集合的功能及使用技巧,需要的朋友可以參考下2015-01-01

