1. 程式人生 > 實用技巧 >javascript 變數提升 函式有嗎 怎麼用?

javascript 變數提升 函式有嗎 怎麼用?

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程式碼之前首先要對程式碼進行編譯,在編譯階段中有一部分工作就是找到所有的宣告,並用合適的作用域將他們關聯起來。

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.實戰練習

經過上面說明,應該對變數提升有了大致瞭解,那麼讓我們再看幾個例子,看看是否真正理解

5.1 例子1:

var getName = function () { 
    console.log('4');
};
function getName() { 
    console.log(5);
}
getName();    

想想這個結果是什麼?

讓我們分析一下這個提升過程,這個程式碼會以近似如下形式進行處理:

function getName() { 
    console.log(5);
}

var getName //被忽略

getName = function () { 
    console.log('4');
};

getName(); 

  1. getName函式和變數getName被提升
  2. 然後var getName因為重複宣告被忽略
  3. 最後函式表示式getName會覆蓋函式getName

所以最後實際執行的是函式表示式getName = function () { console.log('4'); };,結果為’4’

5.2 例子2

function showName() {
  console.log('name1');
}
showName();
function showName() {
  console.log('name2');
}
showName(); 

想想這個結果是什麼?

有了上面經驗,這個就更好理解了,這個程式碼會以近似如下形式進行處理:

function showName() {
  console.log('name1');
}
function showName() {
  console.log('name2');
}
showName();
showName();

所以結果一目瞭然,結果為 name2 name2

6.總結

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

  1. 變數提升,是指在 JavaScript 程式碼執行過程中,JavaScript 引擎把變數的宣告部分和函式的宣告部分提升到程式碼開頭的“行為”。變數被提升後,會給變數設定預設值,這個預設值就是我們熟悉的 undefined。
  2. JS的程式碼執行順序是先進行宣告處理,然後進行賦值等其他操作。
  3. 提升的過程就像是把變數和函式宣告從他們在程式碼中出現的位置“移動”到了最上面。另外只有定義的宣告本身會被提升,而賦值或其他執行邏輯會留在原地
  4. 函式宣告和變數宣告在一起時,函式首先被提升,然後才是變數。