1. 程式人生 > 實用技巧 >js變數提升與函式提升的過程

js變數提升與函式提升的過程

js變數提升與函式提升的詳細過程

先來看兩個栗子,下面的兩段程式碼分別輸出什麼?

// 程式碼段1
function foo() {
  var a = 1;
  function a() {}
  console.log(a);
}
foo();

// 程式碼段2
function foo() {
  var a;
  function a() {}
  console.log(a);
}
foo();

答案是:程式碼段1列印的是1,程式碼段2列印的是 a() 函式。

為什麼會這樣呢?這就涉及到js中的變數提升和函式提升的具體過程了。

1、變數的提升

js是怎麼建立變數的呢?

如下面的程式碼:

var a = 1;
var b = 2;

js在解析上面的程式碼的時候,其實會按照下面的方式進行解析的:

var a;
var b;
a = 1;
b = 2;

所以 js 並不是在我們定義一個變數的時候,宣告完成之後立即賦值,而是把所有用到的變數全部宣告之後,再到變數的定義的地方進行賦值,變數的宣告的過程就是變數的提升。

所以我們看下下面的栗子:

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

上面的程式碼在js的眼中是這樣解析的:

function foo() {
  var a;
  var b;
  a = 1;
  console.log(a); // 1
  console.log(b); // undefined
  b = 2;
}
foo();

所以輸出的 a 的值為1, b的值為 undefined。

變數在宣告提升的時候,是全部提升到作用域的最前面,一個接著一個的。但是在變數賦值的時候就不是一個接著一個賦值了,而是賦值的位置在變數原本定義的位置。原本js定義變數的地方,在js執行到這裡的時候,才會進行賦值操作,而沒有執行到的變數,不會進行賦值操作。

所以變數的提升,提升的其實是變數的宣告,而不是變數的賦值。

2、函式的提升

函式的提升和變數的提升類似,都是提升到作用域的最開始的位置,只不過變數的提升是分兩步的,第一步是變數宣告的提升,第二步是變數的賦值。而函式的提升是直接將整個函式整體提升到作用域的最開始位置,相當於剪下過去的樣子。

3、變數提升和函式提升的順序#

在作用域中,不管是變數還是函式,都會提升到作用域最開始的位置,不同的是,函式的提升後的位置是在變數提升後的位置之後的。

舉個栗子:

下面的程式碼輸出什麼?

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

上面的程式碼在js眼中是這樣解析的:

function foo() {
  var a;
  function a() {}
  console.log(a); // a()
  a = 1;
  console.log(a); // 1
  console.log(a); // 1
}
foo();

所以從上面的栗子可以看到,變數的提升是在函式提升之前的,但是變數賦值的部分是在js原型到變數定義的位置才給變數賦值的,而函式提升是相當於直接剪下到最前面的。

我們再看一個更加複雜一點的栗子:

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

foo();

js是這樣解析的:

function foo() {
  var a;
  var b;
  function a() {}
  function b() {}
  console.log(a); // a()
  a = 1;
  console.log(a); // 1
  console.log(a); // 1
  console.log(b); // b()
  b = 2;
  console.log(b); // 2
  console.log(b);// 2
}
foo();

最後,注意:只有宣告的變數和函式才會進行提升,隱式全域性變數不會提升。

下面的栗子中,b不會進行變數提升。

function foo() {
  console.log(a);
  console.log(b); // 報錯
  b = 'aaa';
  var a = 'bbb';
  console.log(a);
  console.log(b);
}
foo();