本篇為 [JS101] 用 JavaScript 一步步打造程式基礎 這門課程的學習筆記。如有錯誤歡迎指正!
1 | 學習目標: |
JavaScript 是什麼?
JavaScript 是一種物件導向(Object-oriented programming)的腳本語言(Script language),主要用來改進 Web 瀏覽器的客戶端體驗。
腳本語言是一種直譯語言,因不需進行編譯,在撰寫和除錯上較為方便;但缺點是執行效率比不上編譯語言,且無法單獨執行,必須仰賴運行環境。例如:HTML 網頁中的 JavaScript 需要瀏覽器支援才能執行。
直到 Node.js 出現後,提供了 JavaScript 在瀏覽器以外的運行環境。目前實務開發中,通常使用瀏覽器的開發者工具來進行 debug(除錯)。
參考資料:
- JavaScript 基本認識 - JavaScript 入門學習筆記
- 編譯語言 VS 直譯語言- Po-Ching Liu - Medium
- [心得分享] JS = 專為瀏覽器而生的 script (腳本語言)
- JavaScript+jQuery Mobile+Node.js跨平台網頁設計範例教本(電子書)
Node.js 環境建置
進入官網會看到下列文字:
Node.js® is a JavaScript ==runtime== built on Chrome’s V8 JavaScript engine.
(Node.js 一個能執行 JavaScript 的==運行環境==,以 Google Chrome V8 引擎為核心。)
安裝完成後,就可以在 Command Line 輸入指令:
node -v:查看目前 Node.js 版本號
出現版本號就代表安裝成功。

node:直接在 CML 開啟 Node 環境
可在終端機輸入指令。按 Crtl+C 或輸入就.exit 即可退出。

如何執行 JavaScript 文件?
在瀏覽器執行
- 寫在 HTML 文件中的
<script >標籤內

- 用瀏覽器開啟該檔案,點選右鍵選單的檢查,進入開發者工具介面
- 可在 Console 主控台檢視或直接撰寫。通常用來測試代碼的可行性、或直接 debug 抓錯

在 Command Line 執行
vim index.js:輸入 vim 指令建立檔案 index.js,並且編輯內容
也可使用 VSCode、Sublime 等程式碼編輯器來撰寫程式碼

node index.js:在 CML 執行檔案 index.js

基本語法
console.log():將值輸出到瀏覽器控制台
若要輸出字串,需用 ‘單引號’ 或 “雙引號” 括起來。
console.log(’Hello World’) // 輸出值:Hello World
算術運算
+:加-:減*:乘/:除%:取餘數(例如 10 % 3,結果是 1)
邏輯運算
- 邏輯運算子常在 if 判斷式中和布林值(true or false)一起使用
- 在 JavaScript 中會被判定為 false 的值為:
0、""、null、false、undefined、NaN
||:or
只要其中一個是 true 就會返回 true,除非全部為 false。意即只要其中一個條件滿足就成立。

&&:and
全部為 true 才會是 true,否則均返回 false。意即全部條件都必須成立。

!:not
做反向。

|| 與 && 的短路性質
使用最短的路徑來求值,又稱短路求值。只有當第一個運算數的值無法確定邏輯運算的結果時,才對第二個運算數進行求值。例如:
1 | 1. 當 or 的第一個運算數為 true 時,最後結果必定為 true |
在這種情況下,就不需要知道第二個運算數的具體值。也就是短路性質。
範例:
1 | console.log(3 || 10) // output 3 |
var obj = obj || { }; // 如果 obj 存在就維持原樣,如果不存在就給予空物件
var student = name || "小明"; // 如果沒有 name 就用預設為小明。用 || 來設定變數預設值
更多短路邏輯的運用可參考這篇:想知道&&與&及||與|之間的區別嗎? | 程式前沿
位移運算子:<< 與 >>
首先來複習二進位制:
0100 = 2^30 + 2^21 + 2^10 + 2^00 = 2^2 = 4
1000 = 2^31 + 2^20 + 2^10 + 2^00 = 2^3 = 8
<<:將位元往左移一位,可作為乘以 2。

>>:將位元往右移一位,可作為除以 2。若無法整除則會直接捨去。

- 由於電腦使用的是二進位系統,位元運算的速度通常會快於乘法和除法運算。
位元運算
and
or
xor
not
(待補)
遞增、遞減運算子:++ 與 --
1 | var a = 0 // 等號是賦值 |
遞增(++,increment):運算前或運算後「遞增」
遞減(--,decrement):運算前或運算後「遞減」
其中以 ++ 運算子為例:
++ 運算子的回傳值,取決於相對於運算元的位置。
- 先遞增(
++a):用在運算元之前,執行遞增,然後回傳遞增後的值。 - 後遞增(
a++):用在運算元後方,執行遞增,然後回傳未遞增前的值。
1 | var i = 1, j = ++i // i 與 j 兩者皆為 2 |
若以邏輯運算為例:
- 先遞增
1
2
3
4
5
6var a = 0
console.log(++a && 30) // 印出 30,此時 a 為 1
console.log('a:' , a) // 印出 a:1
// 先跑 a+=1,再 console.log(a && 30) - 後遞增
1 | var a = 0 |
值的型態
JavaScript 的資料型態可分為:
- 原始型態(primitive types)
- boolean(真偽值):ture 和 false
- number(數字):例如 1、3.14159、NaN(無效的數字)
- string(字串):例如
'Hello World' - null:沒有值存在(no value)
- undefined:值不存在(absence)
- 其他都屬於物件型態(object types)
- array(陣列):例如 [1, 2, 3]
- function(函式)
- date…etc
typeof <value>:用來判斷參數型態
1 | console.log('typeof true', typeof true) |

在 MDN 網站 列出 typeof 的可能回傳值:

null 使用 typeof 運算子,回傳的結果會是字串 “object”,這指出 null 可被認為是象徵「無物件」(no object)的一種特殊物件值。(參考資料:犀牛書)
這其實是 JavaScript 最初發現的一個錯誤,然後被 ECMAScript 沿用了。現在,null 被認為是物件的佔位符,從而解釋了這一矛盾
(參考資料:你懂JavaScript 嗎?#4 型別(Types) )
變數(Variable)
- 用來暫時儲存資料的地方
- 想像成裝東西的箱子,
宣告變數是將這個箱子取名,加上等號賦值是在裡面裝東西
1 | 其他情形: |
宣告變數
- 不可用數字開頭
- 不可取名為保留字詞,例如 var、function、for
- 變數盡量用語譯化的方式命名,例如 peopleCount、total
- 變數的取名規則需統一,可分為下列兩種:
- 蛇式命名(snake_case):名稱中間的標點以底線連接
var this_is_a_box - 駝峰式命名(camelCase):除了第一個單詞外,後面的單詞首字母均為大寫
var thisIsABox
變數的儲存模型
前面提到變數是像箱子的儲存模型,參考 Huli 寫的這篇文章,舉以下範例來說明其特性:
1 | // 範例一 |
- 即使代表的東西相同,但 A 和 B 其實存放在不同的格子,而 A 和 C 相同。
==與===是去看「格子的內容」是否相等,而不是檢查「所代表的東西」是否相等。
1 | // 範例二 |
- 「往裡面放東西」與「改放全新的東西」是兩件完全不同的事情。
- 一般的變數存資訊,物件存記憶體位置。
- 一般的變數:變數裡面存的內容就真的是那個資訊,例如:數字、字串
- 物件:變數裡面存的內容其實是「指引」,指引存的是記憶體位置,例如:陣列或物件
變數的運算
- 注意值的型態:字串和數字相加時,會變成字串相加
1 | var a = '10'https://www.bilibili.com/video/BV1Hz411i7ph/ |
解決方法:
console.log(Number(a) + b):用Number()將字串轉成數字console.log(parseInt(a, 10) + b):用parseInt()將字串轉換成整數,10 代表預設的十進位
- 注意浮動數誤差:電腦在儲存小數值可能會產生誤差
1 | var a = 0.1 + 0.2 |
萬年經典題:== 與 ===
=:代表賦值== 和 ===:均用來判斷是否相等
1 | var a = 10 == 10 // 會從右執行到左,10 == 10 true |
差別在於 === 會判斷「型態」:
1 | console.log(0 == ’0’) // true |
永遠都用三個等號,如此最能夠避免型態不同而發生錯誤。
陣列(Array)
在寫程式時遇到重複的動作,一定有方法能夠優化。
- 通常用來存放性質相近的資料
- 想像成一個列表物件,裡面含有幾個數值
1 | // 陣列基本操作 |
1 | // 範例練習 |
物件(Object)
- 物件是一批相關的數據或功能
- 通常包含幾個變數及函式,當它們包含在物件中時被稱做屬性(properties)或函式(methods)
1 | // 試著建立一個物件 |

從 Object 的等號真正的理解變數
由 console.log(1 === 1),會回傳 true 這個例子,來判斷下列情形:
1 | console.log([] === []) |
結果卻是:

變數是一個箱子,在放入數字的情況下:
1 | var a = 30 |
但如果在變數裡放入物件,結果卻會如下:
1 | var obj = { |
可以想像成「記憶體位置」:儘管兩個箱子儲存的數值相同,但因記憶體位置不同,指向的元素不同,所以不會相等。

如果換成下列情形:
1 | var obj = { |
兩者理所當然會相等,但若將 obj2.a = 2:更改 obj2 物件中 a 的值,會連 obj 的值也一起更動:
1 | var obj = { |
會發現 obj 的值也一起被更改了。這是因為 obj 和 obj2 指向同一個記憶體位置,指向同一個物件。

但如果 obj2 = {b:1}:obj2 等於一個新的物件,會指向一個新的記憶體位置:
1 | var obj = { |
這是因為「往裡面放東西」與「改放全新的東西」是兩件完全不同的事情。後者會指向一個新的記憶體,可參考下圖理解:

判斷式
判斷式在 JavaScript 中用來==控制流程==。當指定的條件成立時,就會執行後續的指令。判斷式的語法有兩種:if...else 和 switch。
if-else statement
- if 後面的小括號內:條件式,由比較運算子或邏輯運算子組成
- 當條件成立時:會執行 if 大括號內的程式碼:
- 當條件不成立,但也想執行特定動作:在 if 大括號外面使用 else
1 | // 基本架構 |
1 | // 練習:判斷是否為 5 的倍數 |
if-else if statement
- 當判斷情境更複雜時,搭配
else if,用來新增條件判斷 else if可以有很多個
1 | // 基本架構 |
1 | // 練習:判斷是否及格 |
1 | var kg = 60 |
switch case
- 通常用於「有很多條件」要判斷的情況,適合用來處理只有「整數」或「字元」類型的資料
- switch-case 判斷式沒有辦法處理「數值範圍」的問題
- 每個
case 條件:後方都會加上break:用途是停止執行後面的程式碼,否則switch會從匹配的case標籤開始執行到尾端
1 | // 基本架構 |
switch case vs if else
- 效能差異:當比對的參數多時,switch 的可讀性和效能較佳
- 判別差異:switch 在判斷上採取嚴謹模式(同等性由
===運算子判斷),亦即型別也要相同
參考資料:
- switch…case 和 if…else效率比較和優化
- JavaScript 基礎知識-switch & if else 的判別差異
三元運算子(ternary)
- 也可稱作 Conditional Expression(條件表達式)
- 其實就是 if-else 的簡單寫法,適合巢狀結構(建議最多一層)
1 | // 語法: |
以判斷是否及格為例:
1 | // if-else 寫法: |
迴圈(loop)
- 迴圈是程式流程控制的一環,用來重複執行相似的工作
- 必須設定==終止條件==,否則執行時將進入無窮迴圈(可按
Ctrl+C跳出)
do…while 迴圈
- 先執行後才判斷條件,代表迴圈主體至少會被執行一次
1 | // 語法: |
while 迴圈
- 其實就是 do…while 迴圈的變形版本。差別在於 do…while 會至少被執行一次,才進行條件判斷
- 由於迴圈主體需至少執行一次的情況並不常見,通常還是會使用 while 迴圈
1 | // 語法: |
for 迴圈
- 類似 while 迴圈,但 for 迴圈通常用於「已知重複次數」的情況,也就是設定初始值和終止條件
- for 迴圈基本架構,需要三個運算式作為參數:initialize(初始值)、condition(條件判斷)、increment(遞增迴圈變數)
- 三個運算式可省略任何一個,但中間的兩個分號必須存在。例如:
for (;;)會是無窮迴圈
1 | // 語法: |
1 | // 範例: |
1 | // 可和 while 迴圈進行對照: |
break 與 continue
- 用來改變迴圈的執行流程
- 只能用在迴圈或 switch 述句中
break(中斷):中斷整個迴圈語句,也就是「跳出」迴圈區塊
1 | // 範例: |
執行結果如下圖:

continue(繼續):繼續下一次迴圈語句,會忽略在 continue 之後的語句,直接跳到下一次的迴圈開頭
1 | // 範例: |
執行結果如下圖:

函式(Function)
以前學的國中數學 y = f(x) 其實就是一種函式:
1 | y = f(x) |
1 | // 範例: |
最基本的函式結構
在 JavaScript 的函式架構:
1 | function 函數名稱(參數) { |
1 | // 範例: |
範例:在陣列產生 n 個元素的函式
1 | function generateArray(n){ |
也可以不寫 return,寫法如下:
1 | function log(n) { |
範例:印出 1~100 的偶數
小技巧:不知如何解題時,可先從寫下 Function 架構開始
1 | // 先寫出函式架構: |
要產生 1~100 的數字,可能需要迴圈:
1 | function print1to100() { |
需要再寫一個函式,來判斷 i 是否為偶數:
1 | function logEven(number) { |
宣告函式的不同種方式
- 在語法上大致分為 4 種方式,可參考這篇介紹:函數定義 (Function Definition) 的 100 種寫法
- 其中最常使用的為宣告式,以及匿名表達式。
第一種:宣告式(Function Declarations)
- 是最常見的標準寫法。
- 使用
function關鍵字作函數的宣告和定義。
1 | function hello() { |
第二種:匿名函數(anonymous function),或稱匿名表達式
- 前面提到函式也是一種資料型態。因此可先宣告一個變數,再定義一個函數內容放到該變數裡。
- 此方式定義的函數,實際上是匿名函數,只是將函數存在某個變數裡。
1 | var hello = function (a, b) { |
console.log() 括號內可傳入函式
我們在先前的範例,console.log() 都只傳入數字、字串等,但其實也可傳入函式,如以下範例:
1 | function transform(arr, transformFunction) { |
上述範例中,在 transform 函式裡面,double 函式取代了參數 transformFunction,也就是執行 double 函式內部的運算。
也可直接把整組函式丟到 console.log() 括號內,就不需再額外命名。好處是可直接修改函式定義,如以下範例:

引數(Argument)與參數(Parameter)
參數:方法定義中的變數
引數:在呼叫方法時真正傳入的值
1 | // 範例: |
結果如下圖,印出 { '0': 2, '1': 5 }。

Argument 物件
由上述範例可知,arguments 物件其實是種「類陣列物件」。
- arguments[0]:代表第一個引數
- arguments[1]:代表第二個引數
並且具有 length 屬性。
1 | function add(a, b) { |
參考MDN - Arguments 物件,由於 arguments 帶其他屬性,可能因此需要用物件的方式來儲存資訊。
function 傳參數的運作機制(待補)
1 | function swap(a, b) { |
1 | function addValue(obj) { |
例外情況是數字.字串.布林,這三者較簡單,所以在操作的時候會變成直接複製。而較複雜的部份因為使用指向的方式,所以只要有一個地方修改,就等修改其他指向該記憶體位置的操作。
1 | function addValue(obj) { |
在這邊的操作是把 obj 指向了另外一個新的記憶體,等於 obj 指向了另外一個記憶體位置
這種情況以專有名詞稱作:
pass by value
pass by reference. (JavaScript 中沒有)
pass by sharing
可以參考:深入探討 JavaScript 中的參數傳遞:call by value 還是 reference?
return 的作用與使用時機
首先可以把 function 分成兩類:
第一種:不需要知道結果
也就是只需要呼叫 function,但不需知道執行結果的情況。
1 | // 範例: |
1 | // 也可以傳參數進去: |
也可以回傳些什麼,如以下範例,但 return 並不會影響結果。
1 | function sayhello(name) { |
第二種:需要回傳值
例如需要函式進行運算,然後回傳結果。
1 | function double(x) { |
注意:在 function 一旦執行 return 就會跳出,return 以下的任何程式碼都不會再執行。
1 | 舉例 |
常用內建函式
Number 類型的內建函式
可參考:
Number - JavaScript - MDN - Mozilla
Math - JavaScript - MDN - Mozilla
Number():將字串轉數字
1 | var a = 10 |
parseInt():將字串轉整數。預設為十進位,例如:parseInt(a, 10)
1 | var a = 10 |
parseFloat():將字串轉浮點數。也就是有小數點
1 | var a = 10 |
toFixed():取到小數點後第幾位。括號內不輸入就會取整數。可與parseFloat()搭配使用
1 | var a = 10 |
.toString():數字轉字串
或是將數字加空字串(''),因為「數字 + 字串 = 字串」。
1 | var a = 2 |
Number.MAX_VALUE,Number.MIN_VALUE:得知在 JavaScript 可儲存的最大、最小值,若超出這個值,計算就會不精準
1 | console.log(Number.MAX_VALUE) |
Math.PI:圓周率。常數通常用大寫表示Math.ceil():無條件進位,取大於這個數的最小整數
1 | console.log(Math.ceil(3.14)) |
Math.floor():無條件捨去,取小於這個數的最大整數
1 | console.log(Math.floor(10.9)) |
Math.round():四捨五入
1 | console.log(Math.round(10.5)) |
Math.sqrt():開根號
1 | console.log(Math.sqrt(9)) |
Math.pow():次方
1 | console.log(Math.pow(2, 10)) |
Math.random():產生從 0~1 隨機數(不包含 1)
1 | console.log(Math.random()) |
String 類型的內建函式
可參考:String - JavaScript - MDN - Mozilla
toUpperCase,toLowerCase():將字串轉換大、小寫
1 | var a = 'abc'.toUpperCase() |
或是參考 ASCII code 進行轉換:
.charCodeAt():取得字串特定位置的字元 ASCII 編碼
1 | console.log("ABC".charCodeAt(0)) |
String.fromCharCode():將 ASCII 編碼的數字轉換成字元
1 | var str = String.fromCharCode(65) |
利用 ASCII code 進行字串比大小,可判斷該字元為大小寫、是否在條件範圍內:
1 | var char ='J' |
indexOf():可回傳「指定字串」在字串中第一次出現的位置。若找不到則回傳 -1
1 | var str = 'hey hello world' |
replace():取代字串。只能換第一個指定字串。要全換的話可使用正規表達式來達成
1 | // replace()換第一個: |
split():透過「指定分隔符」來分開字串,回傳值為陣列
1 | var str = 'data1,data2,data3' |
trim():移除目前字串開頭和結尾的所有空格
1 | var str = ' data1,data2,data3 ' |
string.length:可回傳字串長度。此指令不是函式
1 | var str = 'hello world' |
可應用在迴圈:
1 | var str = 'hello!' |

Array 類型的內建函式
可參考:Array - JavaScript - MDN - Mozilla
join.():將陣列中所有元素連接成一個字串,預設分隔符是逗號(在 Windows 系統)
1 | var arr = [1, 2, 3] |
map():把陣列中的每個元素帶入指定函式,然後建立一個新的陣列
1 | var arr = [1, 2, 3] |
filter():概念和map()類似,是根據指定的測試函數,從一個陣列中過濾出符合條件的元素,並建立新的陣列
1 | var arr = [1, 2, -3, 5, -7] |
slice():可截取出陣列某部份元素,會建立一個新的陣列
1 | var arr = [0, 1, 2, 3, 4, 5] |
splice():可用來刪除與新增元素,會改變原本的陣列
1 | var months = ['Jan', 'March', 'April', 'June'] |
sort():依照字母順序排列陣列中的所有元素,會改變原本的陣列
1 | var arr = [1 ,30 ,4 ,21] |
Immutable(不可變)
除了物件以外,其他基本型別(primitive types)具有不可變的特性。
1 | var a = "hello" |
以內建函式 toUpperCase() 為例,如以下寫法:
1 | var a = 'hello' |
由於字串具有不可變的特性,不論呼叫什麼函式均無法改變 a 的值,如以下範例:
1 | var a = 'hello' |
而物件型別,例如 Array,呼叫某些函式的時候,有可能會更動到原本記憶體位置儲存的東西:
1 | var arr = [2, 4, 6] |
參考資料:
- JavaScript 初心者筆記: 判斷式 - if…else / switch 篇
- Java中for、while、do while三種迴圈語句的區別介紹
- 迴圈· 從ES6開始的JavaScript學習生活
- JavaScript 基礎:函式的基本介紹- Hugh’s Program learning