JS中的提升總結

1.JS代碼執行順序

我們直覺上會認爲JS的代碼在執行時是由上到下一行一行執行的,但實際並不完全正確,下面的例子會證明:

a = 'haha'
var a
console.log(a)

上面的代碼會輸出什麼呢?

如果按照我們認爲的由上到下一行一行執行,那麼應該輸出undefined,但是實際結果是'haha'

接着再看一個代碼:

console.log(a)
var a = 'haha'

那這個輸出的是什麼?

鑑於上面代碼表現出來的非自上而下的特點,有可能認爲是’haha’。或者有認爲變量a沒有聲明,所以會報錯,但實際結果是undefined

爲什麼會是這樣呢?到底發生什麼?讓我們帶着這個問題看下去

2.變量提升

2.1 編譯

我們要知道,引擎在解釋JS代碼之前首先要對代碼進行編譯,在編譯階段中有一部分工作就是找到所有的聲明,並用合適的作用域將他們關聯起來。

所以總結來說,包括變量和函數在內的所有聲明都會首先被處理,然後纔是代碼被執行

當我們看到var a = 'haha'時候認爲它是一個聲明,但其實在JS中它會被看做兩個聲明,var aa = 'haha'var a是定義聲明,是在編譯階段進行;a = 'haha'是賦值聲明,會留在原地等待執行階段。

2.2 解釋上面問題

接着我們再看回第一個例子:

a = 'haha'
var a
console.log(a)

通過上面說明,可以知道會先處理定義聲明,所以整個代碼會以如下形式進行處理:

var a //在編譯階段進行變量提升
a = 'haha'
console.log(a)

第二個例子也是相同處理

console.log(a)
var a = 'haha'

在這個例子中也會進行變量提升,所以整個代碼會以如下形式進行處理:

var a 
console.log(a)
a = 'haha'

3.函數提升

3.1 基礎用法

看完變量提升,再看一下函數聲明如何進行提升

foo()
function foo() {
  console.log('haha')
}

看完上面的例子,我想大家也能猜到了結果,就是’haha’。
因爲foo函數的聲明被提升了,所以第一行中的調用可以正常執行。

3.2 作用域提升

接下來再看一個例子

foo()
function foo() {
  console.log(a)
  var a = 'haha'
}

這個會輸出什麼呢?
答案是:undefined

在這個例子中,不只有函數提升,還有變量提升。要注意的是,每個作用域都會進行提升操作,所以foo()函數自身也會在內部對var a進行提升,但是只能在這個作用域中進行提升,並不能提升到整個代碼的最上方。

因此這段代碼會以如下形式進行處理:

function foo() {
  var a
  console.log(a)
  a = 'haha'
}
foo()

3.3 函數表達式

foo()
var foo = function bar() {
  console.log('haha')
}

這個代碼執行結果是:TypeError: foo is not a function

這是因爲變量標識foo被提升並分配給所在作用域,所以不會出現ReferenceError的錯誤,但是foo此時沒有賦值,也就是此時foo是undefined,所以執行foo()是對undefined值進行函數調用,因此結果爲TypeError。

這個例子證明了:函數聲明會被提升,但是函數表達式不會被提升

3.4 具名函數表達式

foo()
bar()
var foo = function bar() {
  console.log('haha')
}

從上面我們知道,執行foo()會是typeError,那執行bara()呢?
答案是:ReferenceError: bar is not defined

這是因爲:即使是具名的函數表達式,名稱標識符在賦值之前也無法在所在作用域中使用

因此這段代碼會以如下形式進行處理:

var foo

 foo() //typeError
 bar() //ReferenceError
 
 foo = function bar() {
  console.log('haha')
 }

4. 函數優先

函數聲明和變量聲明都會提升,但是在處理聲明中是 函數首先被提升,然後纔是變量

foo()
var foo 
function foo() {
    console.log(1)   
}
foo = function () {
    console.log(2)
}

這個例子的結果是1。
這個代碼會以如下形式進行處理:

function foo() {
    console.log(1)   
}
foo()
foo = function () {
    console.log(2)
}

注意:var foo雖然會出現在foo()之前,但是因爲重複的聲明,所以被忽略了。

5.總結

通過上面的內容,我們可以進行一個簡單的總結:

  1. JS的代碼執行順序是先進行聲明處理,然後進行賦值等其他操作。
  2. 提升的過程就像是把變量和函數聲明從他們在代碼中出現的位置“移動”到了最上面。另外只有定義的聲明本身會被提升,而賦值或其他運行邏輯會留在原地
  3. 函數聲明和變量聲明在一起時,函數首先被提升,然後纔是變量
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章