---
# System prepended metadata

title: 提升 Hosting
tags: [' hexo部落格', JavaScript]

---

---
tags:  JavaScript, hexo部落格
title: 提升 Hosting
---
# 提升 Hosting
先看看以下這些例子，會產生甚麼結果?

e.g. 1 : console.log 結果會是?
```javascript 
console.log(a);
// ?
```
e.g. 2 : console.log 結果會是?
```javascript 
console.log(a);
var a = 1;
// ?
```

e.g. 3 : fn() 是否執行? 又會產生甚麼?
```javascript 
fn();
function fn(){
  retrun 1;
}
// ?
```

e.g. 4 : console.log 結果會是?
```javascript 
console.log(fn);
function fn(){};
var fn = 1;
// ?
```
---

## 變數提升
當我們宣告變數時，JS 就會先開一個記憶體位置來存放，而它的預設值為 `undefined`，最後賦予我們給這個變數的值。而 JS 的執行順序為由上至下且依序執行，所以範例 1 與範例 2 就會是 : 
e.g. 1 : console.log 結果會是 `a is not defined`
```javascript 
console.log(a);
```
e.g. 2 : console.log 結果會是 `undefined`
```javascript 
console.log(a);
var a = 1;
```
實際上它們的執行順序是這樣 : 
```javascript 
var a;
console.log(a);
a = 1;
```

## 函式提升
在 JS中，它會依據函式定義的位置順序，依序將函式提升到最上方，但在函式內的不會移動到外層。
e.g. 3 : fn() 會執行並 return 1
```javascript 
fn();
function fn(){
  retrun 1;
}
// ?
```
實際上它的執行順序是這樣 : 
```javascript 
function fn(){
  retrun 1;
}
fn();
```
而在函式內的函式不會提升到外層，但是可以呼叫外層的函式 : 
```javascript 
var fnA = () => {
 fnB();
 fnC();
 var fnB = () => 2
}
var fnC = () => 3
fnA();
```
不過，如果將匿名函式賦予到一個變數上，就會根據變數的規則走，而函式提升的優點在於，可以呼叫其他函式，不受執行順序影響，但執行時機會。

## 變數與函式提升的優先程度
在 JS 中，會將優先將函式提升到最上方，接著再宣告變數，而函式的參數傳遞限於函式內並且是最優先，重新宣告它無任何意義。
e.g. 4 : console.log 結果會是 `function fn(){}`
```javascript 
console.log(fn)
// 第一次 console.log(fn) 為 function fn(){}
function fn(){}
var fn = 1
console.log(fn)
// 第二次 console.log(fn) 為 1
```
```javascript 
function fnA(a){
  console.log(a)
  fnB(10)
  function fnB(a){
    console.log(a+3)
  }
  var a = 5
}
fnA(1) 
// 1
// 13
// fnB() 的參數其實跟 fnA 的參數 a 無關，只看 fnB() 真正執行時傳了甚麼值進去
// 若使用 let 宣告 a 會報錯，因為 a 為參數
// Identifier 'a' has already been declared 
```
再舉一個例子看函式是否提升到最上面 : 
```javascript
function fn() {
  a();
  b();
  var x = 2;
  var a = () => {
    x = 100;
    console.log(x)
  };
  function b() {
    x = 200
    console.log(x)
  }
}

fn();
// 執行匿名函式 a 會報錯，因為它還未被賦予值，所以一開始就執行 a() 會是 undefined
// 執行函式 b 可以正常運行，因為它是函式且被提升最上面
```

## Hosting 做了甚麼事
我們打開編輯器撰寫 JS 時，其實是在一個全域環境底下開始寫程式，它同時是全域也是執行環境，當我們宣告一個 function 時，也會同時建立 : 
- 獨立的執行環境 ( Execution Contexts )
- 獨立的變數物件 ( Variable Object )

以下的例子有不同情況 : 
1. 未傳值的參數，其預設為 undefined
```javascript 
// 建立執行環境 Execution Contexts
function fnA(a, b, c){
  console.log(a, b, c)
}
fnA(10)
// 10 undefined undefined

// 建立變數物件 VO 
// {
//   a:10
//   b:undefined
//   c:undefined
// }
```

2. 函式內建立函式，函式若與參數同名會被覆蓋
```javascript 
// 執行環境 Execution Contexts
function fnA(a){
  function a(){}
  console.log(a)
}
function fnB(){return 1}
fnA(fnB)
// function a(){}

// 參數 a 被函式覆蓋掉
// VO variable object
// {
//   a: function a(){}
// }
```
3. 總合以上情況並再次宣告參數然後賦予值
```javascript 
function fnA(a, b, c){
  function b(){}
  console.log(a, b, c)
  // (10, function b(){}, undefined)
  var c = 5
  console.log(c)
  // 5
}
function fnB(){return 1}
fnA(10, fnB)

// 為什麼 c 不是 5 ? 因為它宣告被忽略且呼叫時未被設定成 5
// 在第二次呼叫 c 時，已被設定成 5

// VO variable object
// {
//   a: 10,
//   b: function b(){},
//   c: undefined
// }
```

透過以上例子可以發現，當有執行環境後，就會有變數物件，而它在函式中會做以下三件事 : 
- 若參數值為函式且參數名稱同名則覆蓋成函式
- 若函式內宣告且與參數名稱同名則忽略
- 參數名稱若有設定，預設值為 undefined

回到全域環境後，就更加單純，使用 var 宣告的函式與變數會被覆蓋，若該變數未賦予值時，預設值為 undefined。

再回想一下，當我們寫好 JS 程式碼後，透過瀏覽器執行，其實是開始創建接著執行，在創建(包含編譯)的過程中，**Hosting 就是在建立變數物件的過程。**

```javascript 
// 撰寫 > 創建(包含建立變數物件) > 執行
console.log(a)
var a = 1
console.log(a)
// undefined
// 1
```


## Temporal Dead Zone
目前的範例都是使用 var 來宣告，ES6開始就推薦以 let 或 const 來宣告變數，那麼 let 與 const 所宣告的變數與函式也具有提升特性嗎?
```javascript 
console.log(a)
let a = 1 
// Uncaught ReferenceError: Cannot access 'a' before initialization 
```
上面錯誤的意義是，未初始化變數 a 前，無法存取它的值，為什麼會這樣?
```javascript 
// 執行環境
console.log(a)
let a = 1 
// VO，在變數物件中還是有建立名稱，
// 但按照依序執行的規則變成了未賦予值
// {
//  a: ?
// }
```
為了解釋這個空窗期，於是就有了這個名詞 Temporal Dead Zone。拋出錯誤的目的還是希望開發者的撰寫習慣能改變吧?! 




## 參考來源
> 1. [Huli - 我知道你懂 hoisting，可是你了解到多深？](https://blog.techbridge.cc/2018/11/10/javascript-hoisting/)
> 2. [ShawnL - JavaScript 深入淺出 Variable Object & Activation Object](https://shawnlin0201.github.io/JavaScript/JavaScript-Variable-Object-and-Activation-Object/)
> 3. [江江好 - Day22【ES6 小筆記】變數提升（Hoisting）與暫時死區（TDZ）](https://ithelp.ithome.com.tw/articles/10219518)
