[JavaScript Note] — 淺談 JavaScript #1

Introduction & 前言

筆記系列終於寫到了 JavaScript,想想接觸程式跟前端已經快滿一年了,這一年剛開始的時候很著急想要盡可能地快點學會點什麼,後來發現真的是跟練功一模一樣,除非你是萬中選一的寫 Code 奇才,不穩紮穩打,就會摔得很慘啊!

註:因為前陣子出車禍加上滿多事情突然席捲而來,筆記停了好久,*The F2E 原本也想要寫篇心得文,以至於都沒完成,在寫 Code 的路上慢慢能體會別人說的,這是一件很需要耐心及毅力的事情;你熱愛它,它即熱愛你。*

沒完賽 真羞愧

剛接觸程式的時候連 JavaJavaScript 都傻傻搞不清楚,就一臉萌逼的亂摸亂玩下去了,一路上還是很感謝身邊的人不嫌棄的指導;在剛接觸程式的不久,剛好碰到六角學院舉辦的地下城活動 (沒錯就是上方那張圖),這活動真的幫助自己的 JavaScript 很多,其中自己跌跌撞撞玩了一些東西,發現好多東西都跟 JavaScript 脫離不了關係,直到最近六角學院剛好推出了 **JavaScript 核心篇**,當下思考了不到三秒就下訂了 (放心本篇不是業配 也沒這麼爛的業配 xD)。

購買 [傳送門](https://www.hexschool.com/courses/js-core.html?utm_source=LINE&utm_medium=message&utm_campaign=0807LINE_message#recommend)

接下來就是本篇的出產原因,不免俗的要來一句:

*本篇文章一切皆為筆記作用,有任何錯誤及問題,歡迎理性指教及討論,詳情請查閱公開課程說明書。


Summary & 摘要

本系列將會淺談 JavaScript ,希望給未來自己當作筆記以及剛接觸 JavaScript 的夥伴們,甚至能帶點不一樣觀點或您沒聽過的觀點給大家,所以不會太過深入的討論。

關於淺談的主題將會大致分為下列幾項,項目可能會做更動

  1. JavaScript 執行環境及作用域

  2. 運算子、型別與文法

  3. 物件

  4. 關於 This 那些事

再次補充,其中大多做為筆記作用,並非一定絕對,也希望錯誤之處各位高手能高台貴手,必且不吝嗇的進行指教。

本章討論的主題為下列幾項。

  1. JavaScript 執行環境及作用域

  2. 到底該怎麼寫 JavaScript?

  3. 人生都有第一次 — 錯誤

  4. 作用域

  5. 執行堆疊

  6. 範圍鍊

  7. 提升

  8. 同步與非同步


程式的起點 Hello world.

首先談談 JavaScript 執行環境及作用域

再接觸程式之前你或多或少都會聽說過 HTML CSS JavaScript 三寶,你也知道 HTML 是網頁瀏覽器可以讀懂的語言,CSS 則是拿來美化網頁的語言,那 JavaScript 呢?剩下你想得到複雜的功能其實都是靠 JavaScript 做出來的。

JavaScript 是直譯式語言,這種類型的程式語言,會透過 直譯器(註1) 將程式碼一句一句直接執行,不會事先透過 編譯器 編譯成 **機器碼(註2)**,才能執行,優點是彈性高,缺點是往往都要執行後才能發現錯誤,像是透過只要接觸 JavaScript 這輩子一定擺脫不了的 **console.log()**,後面會提到。

註1:關於直譯器可參考 傳送門,電腦並非那麼聰明,舉個例子來說 var abc = ‘Hello World’; 電腦其實看不懂你要把 Hello World 放到 abc 上,所以必須透過直譯器來一個一個給他型別、用途,電腦才會知道這個要做什麼,詳見下圖。

直譯器編譯過程

註2:機器碼指的即是機器語言( machine language ),簡單說就是 01010101010。


所以到底該怎麼寫 JavaScript?

講了這麼多,所以我該怎麼開始寫 JavaScript 呢? JavaScript 的應用非常的多,前端寫到後端都可以使用。這邊是淺談的原因,先列舉最簡單的使用方式,直接在 HTML 引入 JavaScript 或是 .js 檔。

不囉唆 上菜

值得注意的是, JavaScriptCSS 一樣,會有順序問題,越下面的,就會把上面的給蓋掉,這點對剛開始接觸程式的我常常踩雷啊。引入的方式就很簡·單兩種,在 HTML 裡面直接使用

1
2
<script>我是 JavaScript 內容</script>** // 包起來,或是透過 
<script src=”你的 .js 檔路徑”></script> // 來引入 JavaScript

人生都有第一次

我們前面說到 JavaScript 是直譯式語言,所以錯誤往往都是最後才會知道,寫上我們下面的範例,然後打開你以後寫程式的另一半 F12,點到 console 會發現下圖的 ** eft-hand(註3)** 錯誤。

1
'Hello World' = 666;

錯誤就是這麼顯眼

*註3:典型的錯誤有 LHS( left-hand side)RHS( right-hand side ) 錯誤,即是賦值到左邊變數上 及 取直來自右邊變數上的錯誤。直得一提的是 RHS 並不會提示在 F12console 上,如下圖。*

錯誤就是這麼顯眼且枯燥


放下你的魚 開始拿釣竿

前面講了一堆,這邊就開始,首先你必須先知道 JavaScript 使用時常常要先宣告,宣告什麼呢?這就跟 語法作用域 有關係了。我們必須要了解 JavaScript 是 **靜態作用域(註4)**,基本上 JavaScript 都是由很多函式組成的,如下圖。

**語法作用域**

這邊你必須先懂的兩樣東西,一個是宣告變數的方式 **var let const(後面會解說有什麼不同)**,一個是 **function 名稱()**,請見範例。

1
2
3
4
5
6
7
8
9
10
11
12
13
var abc = 'Hello World';  // 全域

function a() { // 函式區域 (a 是你決定要使用時用來呼叫的名稱, 例: a();)

var def = 'Hello JavaScript';

console.log(abc);

console.log(def);

};

a(); // 呼叫並使用 function

通常我們會透過 var 宣告一個變數去裝東西,然後會有很多 function 去寫我們要做什麼事情,比如說這個 function 我要用來新增錢包的零錢,這個 function 我要用來刪除錢包… 等等,那變數放的位子就很重要了,基本上在函式執行階段如果找不到那個變數(例: abc),它就會往外找,如果還是找不到就會出現上方的紅色錯誤提示

你或許會問,那如果我宣告兩個一樣的變數呢?別急!後面會告訴你。

註4:上方有說到直譯式語言及編譯式語言,那 靜態作用域 就是語法在解析的時候就已經確定作用域,不會再做改變, JavaScript 就是屬於這一種。動態作用域則是變數的作用域在函式調用的時候才決定。


前菜主餐點心

正所謂吃飯有先後(有嗎?),在 JavaScript 的執行也有先後順序,在電腦的執行序裡程式都是一樣一樣要排好隊,一個一個來,那我們怎麼知道這個順序呢?記得前面提到的 function 嗎?在我們執行裡,這些 function 就像要一個一個走近一間餐廳,只是這間餐廳只有入口,先來的人先吃,最後進來的人雖然最晚吃,但是它享有先離開的權利。

為什麼要說到先後順序呢?因為在寫程式時,我們必須時刻了解到你的程式現在會怎麼跑,該怎麼跑。

圖解執行堆疊

這時候你可能會有個疑問,那如果我寫了了 b() 緊接著 a() 呢?會怎麼跑呢?我們有個好夥伴,在寫程式的漫漫之路上雖然寂寞,但是它卻會陪著你一直走下去,沒錯,就是開發者除錯工具 F12,創建好你的練習題目,然後按下 F12 吧。

F12

按下 F12 然後點到 Sources,在點擊右邊的暫停按鈕,之後重新整理,你會發現現在程式碼會停止在你觸發的那行程式碼上。

在這邊你就能清楚地知道執行堆疊啦

依序的按往下的箭頭下一步或是往上的箭頭上一部了解整個運作過程。

透過 Call Stack 及 Scope 清楚看見執行堆疊


關於範圍鍊

為什麼上方會說到執行堆疊呢?因為在寫 JavaScript 的時候,還有一點我們必須非常注意,就是我的程式碼影響的範圍多遠?下方的示意圖簡單地表示,雖然我們有宣告兩個一樣的變數 abc ,但是 在哪裡 使用它會得到不同的結果,像是在 function a 的時候因為它在 function a 的範圍內找不到 abc 所以他會直接向外面那層搜尋 abc ,最後結果即是 66666


說好的順序呢?

前面說了堆疊順序,但總會讓人覺得,好像毫無順序可言,而且其實我把要呼叫的程式碼隨便丟,似乎都會正常的跑。其實這跟本篇會說到的重點之一有關,就是提升(Hoisting)。

在上方我們有提到 執行堆疊,那在執行環境中其實程式已經幫我們做了兩件事情,其中一個是 創造,一個是 執行,簡單說創造的時候它已經幫你把程式順序把整理好了。

在前面如果你有試著用 F12 工具去執行會發現,執行到宣告的地方,變數沒有立即的被賦值,卻出現了 undefined,這是為什麼呢?說到這邊就要講講關於記憶體的部分。

創造階段

再創造階段時變數都會先被記錄下來,但是值都是 undefined(註5) ,比較不同的是 函式陳述式 會被完整的先記錄下來並且做提升。一直到執行階段,變數才會賦值上去。

註5:關於 undefined 及 not defined 的差別要注意,一個是已經在創造階段宣告變數名稱但是沒有賦值,一個是在創造階段沒有宣告變數名稱也沒有賦值。

說完記憶體來談談同步與非同步

上方我們有提到 執行堆疊,那在 JavaScript 中如果你要讓某個程式碼晚一點在執行就必須靠非同步來執行,直接來看下面一張圖。

事件佇列事件佇列

因為 Javascript 是 單執行緒,一次只能做一件事情,如果你想要讓它依照你的順序去執行,就必須靠事件佇列來解決,所有非同步內容丟進事件佇列內,最後執行完本次的執行環境才會執行事件佇列的內容。


Conclusion & 結論

其實應該算本章的小結論,這次筆記其實想記錄很多東西,但是有些又覺得不需要特別去紀錄,所以整理起來或許會有點亂,但是在寫文章的過程或多或少又能整理到自己一些思緒,我覺得這次收穫最大的。

在寫這篇文章的時候突然想起,剛接觸程式時不懂 **Javascript(雖然現在也不懂)**,胡亂寫一通,竟然還是跑得起來,某些程式碼也沒有離預期太遠,不過俗話說的好,基礎沒打穩,等你上戰場後就會原形畢露,這段時間覺得自己還是應該好好再次精進 Javascript 的能力。

關於 Javascript 還有很多會慢慢寫筆記,最近常常有種怠惰的感覺,或許是剛開始什麼都不會,進步得很快,但是像那句老話,你學的越多你就越不懂,也或許碰到了學習階段的檻。學習急不得,我會告訴自己努力加油還要保持自己的初衷。To be continued.


參考網站