---
# System prepended metadata

title: 提升 (Hoisting)
tags: [JavaScript - 執行環境、作用域]

---

###### tags: `JavaScript - 執行環境、作用域`

# 提升 (Hoisting)

* 當建立一個執行環境執行程式碼時分為兩個階段，前面只講到執行階段
 1. 創造環境階段
    當宣告變數時會給予變數一個記憶體空間此時還沒賦予值所以內容為undefined
    key : value
    var x = undefined;
2. 執行階段
   執行皆會會將值賦予給左邊的變數
   x = 'apple';

![](https://i.imgur.com/P1Iv2zx.png)


### 創造環境階段 :
準備好記憶體空間 ( 此階段為提升 )
![](https://i.imgur.com/ew4P5ra.png)

### 執行階段 :
將值賦予上去
![](https://i.imgur.com/9H6WTpA.png)

### 涵式陳述式在創造環境階段就會優先載入 :
所以在創造環境階段直接就可以執行
![](https://i.imgur.com/t1muKji.png)

上列為函式陳述式 ( statement )，涵式表達式 ( expression ) 寫法如下，不能像函式陳述式一樣創造環境階段就可執行，他會像宣告變數時一樣會先經過創造環境階段在經過執行階段才會將函式賦予給左邊的變數

```javascript=
console.log(callName); // undefined
var callName = function (params) {
	// ...
}

// 實際上是這樣
// 創造環境階段
var callName; // undefined
// 執行階段
console.log(callName); // undefined
callName = function (params) {
	// ...
}
console.log(callName); // f(params){}
```

如果有變數和函式同時存在，函式會優先被提升再來是變數
```javascript=
function callName(){
	console.log('呼叫小名1');
}

var callName = function () {
	console.log('呼叫小名2');
}
callName(); // 呼叫小名2

//實際上是
// 創造環境階段
function callName(){
	console.log('呼叫小名1');
}
var callName
// 執行階段
callName(); // 呼叫小名1，因為 var callName 重複宣告是沒有用的
callName = function () {
	console.log('呼叫小名2');
}
callName(); // 呼叫小名2，因為第二個callName會覆蓋掉第一個callName
```

### ES6的let、const其實也有提升，只是表現出來的特性不一樣，也就是創造階段的生命週期不同

創造環境階段時，var會提升到執行環境頂部時會同時宣告變數並賦予undefined派記憶體空間，所以我們可以得到undefined

而let提升時只會將變數提升到執行環境頂部但沒有初始化指派記憶體空間(賦予undefined)，所以我們得到錯誤: Uncaught ReferenceError: Cannot access 'a' before initialization，直到離開創造階段到執行階段才會賦予undefined

執行環境被創建時，變數就被建立了，但是一直到創造環境的 'let a' 之前都沒辦法訪問，這就是 TDZ。
![](https://i.imgur.com/dcjA9to.png)

- `let` 在創造執行環境階段時，不會被賦予初始值 `undefined`
- 創造執行環境階段後，對於 `let` 宣告如果沒有賦值，執行到該行時則賦值該變數為 `undefined`

下面一些生命週期的圖片就可以很清楚了解 : 
![](https://i.imgur.com/C6AyYaO.png)

![](https://i.imgur.com/wIJWIG9.png)

![](https://i.imgur.com/4eKPK4p.png)

