JavaScript 執行環境與作用域


Posted by hoyi-23 on 2021-05-16

☛ JavaScript 如何運作 ?

我們撰寫的JS程式碼,需要透過電腦解譯後才能使用。
程式語言分為 直譯式語言 與 編譯式語言。

  1. 編譯式語言 - 撰寫原始碼後,預先編譯生成代碼後才會執行,好處是,當在預先編譯時即可除錯,效能通常較好。
  2. 直譯式語言 - JavaScript 就是屬於直譯式語言(interpreted language) - 執行前沒有經過編譯,原始碼透過直譯器生成代碼,錯誤會直接反映在環境中(eg: console),優點是彈性較高。

直譯的過程

參考網站

var name = "andy" ;
  1. 語法基本單元化 (Tokenizing) - 將所有的字詞解析出來
  2. 抽象結構樹 AST(Abstract Syntax Tree)
  3. 代碼生成
  4. 運行

☛ 執行的錯誤情境 LHS /RHS

LHS

left-hand side 用於賦予值到左側的變數上

var name = "Andy";

錯誤的情境 : 當左側不是一個變數時..

'bella' = "Andy"; //錯誤  Invalid left-hand side in assignment 編譯過程中發生的錯誤

練習題 : 為甚麼會出現LHS錯誤?

//<P>Hi</p>
function ClickThis(){console.log('click')}
$('p').click() = ClickThis; //執行的函式不能再被賦予值。
//正確寫法
function ClickThis(){console.log('click')}
$('p').click(ClickThis); //執行的函式不能再被賦予值。

當有LHS錯誤時,特別注意左邊的值是否可以被賦予。

RHS

right-hand side 取值來自於右側的變數上

var name = "Andy"; //LHS
var man = name ; //使用RHS取的name的變數,並用LHS賦予值到變數man
console.log(name) //RHS

錯誤的情境 :

var name = "Andy"; //LHS
console.log(na)  // referenceError : na is not defined. 在執行階段發生錯誤

在JS的運行過程中,若有一個錯誤未修正,則無法繼續運行!!!

☛ JS-語法作用域(Lexical scope)

作用域又分為兩種:

  1. 靜態(語法)作用域 - 變數的作用域在語法解析時,就已經確定作用域,且不會再改變,JS就屬於此。
  2. 動態作用域 - 變數的作用域在函式調用才決定。
    //靜態作用域示範 - 執行前就確定作用域
    var value =1; // 全域作用域
    function fn1(){
     console.log(value);}
    function fn2(){
     var value = 2; //函式作用域
     fn1();
    }
    fn2(); // 1
    
    //動態作用域示範 - 函式調用時才決定作用域,所以會在調用的地方往上一層的函式尋找。
    var value =1; // 全域作用域
    function fn1(){
     console.log(value);}
    function fn2(){
     var value = 2; //函式作用域
     fn1();
    }
    fn2(); // 2
    
    以JS來看,因為JS是靜態作用域,因此答案為 1 。

JS作用域是層層向內

全域 - 內層
當變數在內層找不到時,會向外層找,若全域也找不到,就會出現undefined.

☛ 執行環境與執行堆疊

執行環境

當我們在執行一個函式時,會產生一個執行環境(限制作用域/this)。執行一次就會產生一個執行環境,並產生自己的變數。
在還未執行函時前,不會產生執行環境,亦不會產生變數。

全域環境global

除了函式會產生執行環境,全域也有屬於自己的全域執行環境,當網頁或後段node.js開啟時,執行環境就會建立並同時產生window或global的變數以及this(window or global === this ,但this的指向會隨環境改變)。

執行堆疊 Execution Stack

使用console-resouce查看每個步驟

  1. 網頁開啟 - 全域執行環境
    運行doNow() - doNow()執行環境
    運行sayHi() - sayHi()執行環境
    執行環境一層一層堆疊,執行完成後,一層一層離開。
    function sayHi(name){
     var greeting = 'hi';
     return greeting + '' + name;
     }
    function doNow(){
     var mom = '媽媽';
     console.log(1, sayHi(mom));
     }
    doNow(); // hi 媽媽
    
  2. 網頁開啟 - 全域執行環境
    執行openDoors();
    執行openTheDoors(1); //開第1扇門
    openTheDoors退出
    執行Forloop
    執行openTheDoors(i);//開第2扇門
    openTheDoors退出
    ... 重複執行與退出 ...到次數執行完畢
    退回 openDoors();
    退回 全域
    function openTheDoors(){
    return '開第' + num + '扇門';
    }
    function openDoors(){
    openTheDoors(1);
    for (Var i=2;i<5;i++){
       openTheDoor(i);
    }
    openDoors();
    

☛ 範圍鍊 Scope Chain

當函式內部本身沒有所需變數時,向外尋找的範圍,尋找的範圍與執行環境沒有關聯。
練習: person這個變數,在不同的環境下執行,結果分別為何?

var person ="mon";
function sayHi(){
    console.log('hi'+''+person);
}
function GoOut(){
    var person = "dad";
    function meetAuntie(){
        var person = "auntie";
        console.log('hi'+''+person);
    }
    sayHi();
    meetAuntie();
}
sayHi(); //"hi mon"
GoOut(); //"hi mon" "hi auntie"

步驟:
1.執行sayHi(); //"hi mon"
函式內部沒有person變數,向外層(全域)尋找。
3.執行GoOut();
4.執行GoOut裡面 外層的sayHi(); //"hi mon"
GoOut的執行環境不影響sayHi()的範圍鍊。
5.執行GoOut裡面的meetAuntie(); //"hi auntie"
如果今天meetAuntie();內部沒有person變數,就會向外層尋找變//"hi dad"


#js #javascript #lexical scope #scope







Related Posts

迭代陣列 for...of, filter( )

迭代陣列 for...of, filter( )

JavaScript Drum Kit

JavaScript Drum Kit

BTC White Paper解讀

BTC White Paper解讀


Comments