---
# System prepended metadata

title: JS 核心 1 - 執行環境、作用域 (上)
tags: [JS 核心, ' Javascript']

---

---
title: 'JS 核心 1 - 執行環境、作用域 (上)'
tags: JS 核心, Javascript
description: 2021/02/02
---

JS 進階 -- 執行環境、作用域 (上)
===

## JavaScript 是如何運行的
我們撰寫JS無法直接被瀏覽器或電腦給閱讀，要經過**解譯**才能運行這段程式碼。  
* **編譯式語言 :** 編譯時可除錯，效能較好。  
![](https://i.imgur.com/HUMgW3l.png)

* **JS 屬於直譯式語言 :** 執行前是沒有經過編譯的。 彈性高，不須預先定義型別。  
![](https://i.imgur.com/5fDZZTS.png)

* **JS 直譯器轉換過程**  
![](https://i.imgur.com/xlGVwoT.png)


## 執行的錯誤情境 LHS, RHS
JS 執行時，會有一些與取值和賦值相關的常見錯誤。
![](https://i.imgur.com/kKFatVJ.png)
```
var ming = ‘小明’; // 屬於LHS, 因為我們將小明賦予到左側的變數ming上
console.log(ming); // 屬於RHS, 從右側變數ming取值
var man = ming; // 等號右邊是RHS取值，將ming的值賦予給變數man則屬於LHS
```

* LHS : 將值賦予給左邊的變數，因此當左邊並不是個變數時，就會出現 LHS error。
```
'小明' = 1;
```
* RHS : 右邊變數沒有被定義，無法找到此變數。
```
var ming = '小明';
console.log(min);
```

## 語法作用域(Lexical scope)
### 語法作用域又分為 動態作用域 和 靜態作用域。 (JS 採用靜態作用域)

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

### JS 作用域在函式裡面
```
function callName(){
  var Ming = '小明';   // 若在函式內宣告變數，外層讀不到
}
callName();
console.log(Ming);    // Ming is not defined 
```

### JS 作用域是一層一層向內的 (外圍是全域作用域) (內層都是由function (  ) 所包著)
JS 屬於靜態作用域  ( JS 是語法作用域 → 程式撰寫好時就確定了)  
![](https://i.imgur.com/uUJx6En.png)

```
var value = 1;         // 作用域為外層
function fn1() {
  console.log(value);  // 呼叫value時向外查找，找到全域變數var value = 1;
}
function fn2() {
  var value = 2;       // 這個值只會在fn2(  ) 裡面
  fn1();
}
fn2(); // 1
```

## 執行環境與執行堆疊
### (函式) 執行環境
一個函式的執行，就會產生一個屬於自己的執行環境，而執行環境有**限制作用域**的功用，
並且會**有自己的 this**。
### (全域) 執行環境
全域環境在網頁一開啟或後端node.js一開啟時，執行環境已建立，自動產生window (使用瀏覽器) 、 global (使用node.js) 的變數，也會產生 this。  
<span class="red">this會隨著執行環境而有所不同。</span>


### 執行堆疊
執行堆疊 **(和呼叫函式的位置有關係)**，執行環境是一層一層堆疊起來的。  
最後也是一層一層離開，最後只剩下全域的執行環境。  

![](https://i.imgur.com/QwSDDBr.png)
1. 首先，在瀏覽器開啟時，全域執行環境就會被建立。
2. 接著我們執行doSomething函式，所以doSomething的執行環境被生成，且堆疊在全域執行環境之上。
3. 在doSomething中我們又執行了sayHi，因此sayHi的執行環境被生成，且堆疊在doSomething執行環境之上。

```
function sayHi(name){
  var greeting = 'hi'; 
  return greeting + ''+ name;
}
function doSomething(){
  var mom = '老媽';
  console.log(1, sayHi(mom));
}
doSomething(); // 1, hi 老媽
```
函式沒有透過呼叫是不會執行的，而執行環境可透過不斷呼叫產生。  
執行順序：
openDoors();
openTheDoor(1); // 傳入1 回傳 '開第 1 扇門'  
for迴圈 i=2 // 傳入2 回傳 '開第 2 扇門'  
for迴圈 i=3 // 傳入3 回傳 '開第 3 扇門'  
for迴圈 i=4 // 傳入4 回傳 '開第 4 扇門'  
for迴圈 i=5 // 不符合條件 i < 5, 離開迴圈
```
function openTheDoor(num) {
  return '開第 ' + num + ' 扇門';
}
function openDoors() {
  openTheDoor(1);
  for (var i = 2; i < 5; i++) {
    openTheDoor(i);
  }
}
openDoors();
```

## 範圍鍊
> <span class="red">**範圍鍊是依據函式文法本身來決定，與執行環境無關**</span>

### 範圍鍊概念
* 當函式本身沒有這個變數的時候，就會向外層做尋找。
* 若是函式內的變數找不到值，就會往外一層函式查找，直到全域環境有找到或是沒找到 (undefined)。

:bulb: **範例一** : 
1. sayHi( ) 本身沒有person這個變數，所以會向外尋找，person 指向全域的老媽。 
2. 此執行環境 doMorningWork( ) 並不會影響sayHi( ) 的範圍練，所以 sayHi( ) 會向外尋找。
```typescript
var person = '老媽';
function sayHi(){
    console.log('hi' + person);
}
function doMorningWork() {
    var person = '老爸';
    function mettAuntie() {
        var person = '漂亮阿姨';
        console.log('hello~ ' + person);
    }
    sayHi();
    // mettAuntie();
}
doMorningWork();  // hi 老媽
```
:bulb: **範例二** : sayHi( )隱藏，mettAuntie( )打開
1. 此時 meetAuntie()的 person 會指向 '漂亮阿姨'，因為自己本身就有宣告這個變數。
2. 同上一個情境，若meetAuntie() 沒有宣告 person ，此時 person就會指向外面一層的 '老爸'，再若是沒有找到，最後就會指向全域宣告的 person = '老媽'。




## :+1: 相關參考文件 
:::info
[執行環境與堆疊](https://medium.com/@yining1204/javascript-%E6%A0%B8%E5%BF%83%E7%AF%87-%E5%AD%B8%E7%BF%92%E7%AD%86%E8%A8%98-chap-9-%E5%9F%B7%E8%A1%8C%E7%92%B0%E5%A2%83%E8%88%87%E5%9F%B7%E8%A1%8C%E5%A0%86%E7%96%8A-ca7c5b4ca17c)  
[JavaScript核心篇系列文](https://ithelp.ithome.com.tw/articles/10213660)
:::

## :memo: 學習回顧 
:::info
* 編譯式語言 : 編譯時可除錯，效能較好。
* 直譯式語言 : 執行前沒有經過編譯。 彈性高，不須預先定義型別。
* 執行的錯誤情境
    * LHS : 用來賦予值至左側變數上。
    * RHS : 取值來自右邊的變數。
* 語法作用域
    * 靜態作用域 : 語法解析時就已確定作用域，且不會再改變。
    * 動態作用域 : 作用域在函式調用時才決定。
* JS 作用域在函式裡面 : 是一層一層向內的 (外圍是全域作用域) (內層都是由 function( ) 所包著)。
* 執行環境 : 每執行一次便會產生一個新的執行環境，this 會隨著執行環境不同而改變。
* 執行環境的生成與堆疊 (Execution Stack) 與函式的宣告順序無關，而是與呼叫的順序相關聯。
* 範圍鍊是依據函式文法本身來決定，與執行環境無關。
:::



<style>
.red {
  color: red;
}
.green {
  color: green;
}
</style>