# 小細節(ㄧ)JavaScript基本觀念:認識不同的資料型別、運算子和重要概念 ## 不同execution context的變相不會互相影響-了解function背後運作的邏輯 在JavaScript中要執行一段函式(function)並不困難,只要在函式的最後加上括號 ( ) 就可以了,在英文中,我們把執行函式的執行這個動作稱做invoke。但是,我們需要進一步了解函式運行時背後的邏輯,了解背後運作的原理,我們才可以避免不必要的錯誤產生。 首先,Global Execution Context(A wrapper to help manage the code that is running)會被建立,這時候會一併建立this、global object (window),在程式開始執行的過程中,function a 和 b 由於 **hoisting** 的緣故會先被建立在記憶體中,接著才會開始逐行執行程式。 ```javascript= function b() { var myVar; console.log("1", myVar); } function a() { var myVar = 2; b(); console.log("2", myVar); } var myVar = 1; console.log("3", myVar); a(); console.log("4", myVar); // 3 1 // 1 undefined // 2 2 // 4 1 // 即使執行完 a()後再去呼叫一次myVar,一樣會得到"1的結果, 變數彼此之間不會影響 ``` 由上面的例子,我們可以知道,我們是在不同的execution context中分別去宣告變項myVar,因此在不同的execution context,變項彼此之間不會影響,所以雖然這三個變項都叫做myVar,但其實是三個不同的變項。 由於我們是在不同的execution context中去宣告變項,所以這其實是位於三個不同execution context中的變項,所以即使我們是在執行完a( )後再去呼叫一次myVar,一樣會得到"1"的結果。 ## Scope Chain和outer environment的概念 ```javascript= function b() { console.log(myVar); } function a() { var myVar = 2; b(); } var myVar = 1; a(); // 1 ``` #### 了解Outrt Environment的概念 如果要呼叫一個在該exection context中沒有的變項時,它會往它的outer environment去找。 因此雖然程式執行的過程中會先執行global execution context(myVar = 1),接著執行function a(myVar = 2),最後才執行function b。但是對於function b來說,它的outer environment就是最外層的global environment,所以找到的是var myVar = 1,而不是function a裡面的var myVar = 2。 那麼,要怎麼區分誰是誰的outer environment呢?其實最簡單的方法就是直接從程式碼的相對位置來看,誰在誰的外面就可以了,也就是所謂的Lexical Environment (where something sits physically in the code you write): 我們可以把定義變項的地方畫一條直線(圖中淺黃色處),越往裡面就是越內層, 因此對於我們執行console.log的地方來說,它的外層(outer environment)就是var myVar = 1的地方,用這樣的方法,就可以判斷誰是誰的outer environment了!  #### Scope Chain範圍鍊 Scope Chain就是在找變項的過程中,從內層找到外層,直到最外面的global environment的這條鍊,就叫做Scope Chain。 按照上面的例子來說,就是先從function b()開始,在function b()裡面找myVar這個變項,可是找不到,因此便繼續往外層找,在最外層的global environment中,找到了myVar = 1。 #### 把function b搬到function a裡面: ```javascript= function a() { function b() { console.log(myVar); } var myVar = 2; b(); } var myVar = 1; a(); // 2 b(); // b is not defined ```  **而從b() --> a() --> global environment這樣的鍊,就稱作Scope Chain** ## asynchronous 和 event queue ```javascript= /*Copy from JavaScript: Understanding the weird part*/ function waitThreeSeconds(){ var ms = 3000 + new Date().getTime(); while(new Date() < ms){} console.log("finished function"); } function clickHandler(){ console.log("click event!"); } document.addEventListener('click', clickHandler); console.log("started execution"); waitThreeSeconds(); console.log("finished execution"); // started execution // finished function // finished execution // (三秒後) click event! ``` 基本上JavaScript在執行程式的時候是synchronous的,如果程式是逐行執行,那為什麼它可以監控瀏覽器的一些事件(event)呢?像是偵測滑鼠的點擊、滑動等等這類的非同步呼叫(asynchronous callback)。 execution context的概念,在JavaScript執行asynchronous callback的過程是這樣的,它會先把所有的execution context的內容執行完畢後,才去執行"event queue",event queue裡面就包含像是滑鼠點擊或是http request的內容。 從上面的例子中我們可以發現,JavaScript Engine確實會先把execution context的內容執行完畢,接著才去執行"event queue"中的內容,所以雖然我們是在程式執行的一開始點擊滑鼠,但是"click event"的訊息卻是在"finished execution"出現後才顯示 ## Precedence(優先性)和Associativity(關聯性) ```javascript= var a = 4 + 5 - 7; var b = 2, c = 3, d = 4; b = c = d; console.log("a" + a); // a2 console.log("b" + b); // b4 console.log("c" + c); // c4 console.log("d" + d); // d4 ``` [Operator precedence](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence) 其中我們透過將不同的運算子賦予不同的Precedence來決定哪一個部分要先執行,當兩個運算子的Precedence都一樣的時候,我們就要進一步決定,那麼應該要由左至右判讀,還是由右至左判讀,這個部分則是透過Associativity來決定。 在Mozilla的網站中,即整理了關於JavaScript 中Precedence和Associativity的表,從中可以看到precedence越高的項目會越先執行,但當precedence一樣的時候,則會透過accociativity來決定。 Mozilla Operator precedence的網站可以看到,等號是屬於"Assignment",當它們都具有相同的precedence時,它的associativity是**right-to-left**。 ```javascript= var b = 2, c = 3, d = 4; b = c = d; b = c = 4; b = 4 ``` ## coercion(強迫)的實際使用-- if判斷式 ```javascript= var a; if (a) { console.log("a"); } else { console.log("empty"); } // empty ``` a會是undefined在Boolean()會是false,因此if(a) 就會是false,答案就會是 empty。 ## coercion(強迫)的實際使用-- 建立函式預設值(default value) ```javascript= function greet(name) { console.log("Hello"+name); } greet("world"); // Hello world ``` 如果在函式沒有帶入任何的值 ```javascript= function greet(name) { console.log(name); console.log("Hello"+name); } greet(); // undefined // Hello undefined ``` #### 利用coercion這樣的特性建立預設值(default value) ```javascript= function greet(name) { name = name || "<Enter your name here>" console.log("Hello"+name); } greet(); // Hello<Enter your name here> ``` Precedence和Associativity的觀念,因為 "||" 比 "=" 擁有更高的precedence,所以會先執行"||"的部分,而且"||"是由左至右讀取。
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up