變量提升
準確的說應該是 變量聲明提升
// 例1
a = 100
var a
console.log(a) // 100
// 例子2
console.log(a) // undefined
var a = 300
conole.log(a) // ReferenceError: a is not defined
a = 300 // 因爲 a 並沒有聲明var 所以不提升
通過前面的3個代碼片段來深入探討一下js的提升。
包括變量和函數在內的所有聲明都會在任何代碼被執行前首先被處理。
當你看到 var a = 100; 時,可能會認爲這是一個聲明。但 JavaScript 實際上會將其看成兩個 聲明:var a;和a = 100;。第一個定義聲明是在編譯階段進行的。第二個賦值聲明會被留在 原地等待執行階段。所以第一個例子中的代碼會進行如下處理
// 例1的編譯執行過程
var a
a = 100
console.log(a) //100
var a
console.log(a) //undefined
a = 300
這個過程就叫作提升, 變量聲明在編譯的時候從它們在代碼中出現的位置被“移動” 到了最上面。 注意:只有聲明本身會被提升,而賦值或其他運行邏輯會留在原地。
函數提升
準確的說應該是 函數聲明提升, “聲明”這兩個字很重要哈!!
demo();
function demo() {
console.log( a ); // undefined
var a = 2;
}
上面的代碼demo()函數提升了
function demo() {
var a // 注意每個作用域都會進行提升操作!
console.log( a ); //undefined
a = 2
}
demo()
可以看到,demo()作用域裏面也會進行提升,每個作用域都會進行提升操作!
函數聲明會被提升,但是函數表達式卻不會被提升。
demo() // TypeError: demo is not a function
var demo = function() {
var a = 2
console.log(a)
}
上面這個代碼片段不會跑出 reference error 是因爲 demo 變量聲明被提升到了頂部, 但是 demo 此時並沒有賦值(如果它是一個函數聲明而不是函數表達式,那麼就會賦值)。demo() 由於對 undefined 值進行函數調用而導致非法操作, 因此拋出 TypeError 異常。
demo() // TypeError: demo is not a function
demo1() // ReferenceError: demo1 is not defined
// 具名的函數表達式demo1,名稱標識符在賦值之前也無法在所在作用域中使用
var demo = function demo1() {
console.log("demo1 func")
}
上面的代碼被提升之後的可以理解爲
var demo
demo() // TypeError: demo is not a function
demo1() // ReferenceError: demo1 is not defined
demo = function() {
var demo1 = this
}
函數優先
函數聲明和變量聲明都會被提升, 函數會首先被提升,然後纔是變量
demo() // demo
var demo // 這個在demo函數聲明前面,但是函數提升會優先,但它是重複的聲明因此被忽略了
function demo() {
console.log("demo")
}
// 儘管重複的var聲明會被忽略掉,但出現在後面的函數表達式還是可以覆蓋前面的。
demo = function () {
console.log("demo1")
}
demo() // demo1
demo(); // "b"
var a = true;
if (a) {
function demo() { console.log("a"); }
} else {
function demo() { console.log("b"); }
}
注意: 但是在node.js中上面的代碼試了一下,這裏會拋出TypeError: demo is not a function。 node.js 版本v10.0.0. 有時間再去研究一下,應該是node.js 對if 塊 進行了函數提升的限制, 只提升變量demo。 如果有人知道答案可以留言給我,最好給個連接地址啦。
總結:
無論作用域中的聲明出現在什麼地方,都將在代碼本身被執行前首先進行編譯處理。 可以將這個過程形象地想象成所有的聲明(變量和函數)都會被“移動”到各自作用域的最頂端,這個過程被稱爲提升。
聲明本身會被提升,而包括函數表達式的賦值在內的賦值操作並不會提升。 要注意避免重複聲明,特別是當普通的 var 聲明和函數聲明混合在一起的時候,否則會引起很多危險的問題!