Spread Syntax and Rest Parameters
Spread Syntax
- spread syntax 是擴展 array 中的元素 (使用在 array 和 function invocation)
- 使用在 array
範例 1:
let num1 = [1, 2, 3];
let num2 = [4, 5, 6];
console.log([...num1, ...num2]);
結果為: [1, 2, 3, 4, 5, 6]
範例 2:
const parts = ["肩膀", "膝蓋"];
const otherParts = ["頭", ...parts, "身體", "腳"];
console.log(otherParts);
結果為: [ '頭', '肩膀', '膝蓋', '身體', '腳' ]
- 使用在 function invocation
範例 :
function sum(a, b, c, d, e) {
return a + b + c + d + e;
}
let myArr = [1, 2, 3];
console.log(sum(10, ...myArr, 5));
結果為: 21
- 利用 spread syntax 來複製 array (非 copy by reference)
範例 :
const arr = [1, 2, 3];
const arr2 = [...arr];
arr.push(4);
console.log(arr);
console.log(arr2);
結果為: [ 1, 2, 3, 4 ]
[ 1, 2, 3 ]
Rest Parameters
- Rest Prarmeters 和 Spread Syntax 的語法幾乎一模一樣。
- Rest Prarmeters 是收集多個元素並將他們壓縮為單個 JS array。
- 使用在 function definition
範例 1:
function sum2(...theArgs) {
console.log(theArgs);
}
sum2(1, 2, 3, 4, 5, 6, 7); // 壓縮為單個 JS array
結果為: [ 1, 2, 3, 4, 5, 6, 7 ]
範例 2:
function sum3(...theArgs) {
let total = 0;
for (let i = 0; i < theArgs.length; i++) {
total += theArgs[i];
}
return total;
}
console.log(sum3(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
console.log(sum3(1, 2, 3, 4, 5));
結果為: 55 和 15
Primitive, Reference Data Types
- primitive data type 不是 Object,都沒有自己的 attributes 和 methods。 此外,裝有 primitive data type 的 variable 確實擁有數值,而不僅僅是對其數值的記憶體位置的 reference。
- Object 和 array 都是 Reference Data Type。Reference Data type 變數中,儲存的值是 Reference ,也就是記憶體的位址,指向儲存真實內容的記憶體區塊的位置。
Primitive Coercion(強迫)
- JavaScript 會自動將 primitive data 放入一個 wrapper object 中, 讓它們可以繼承 wrapper object 的屬性和方法, 例如: string.length 屬性 or number.toFix() 方法。
- 這種自動裝箱行為在 JavaScript 代碼中是不可以觀察到的,這就是 primitive coercion。
- 我們也可以自行製作 wrapper object ,但不建議如此做,因為會佔大量 RAM。<8-3範例-時間宣告比較>
let name = new String("Wilson");
console.log(typeof name);
結果為:object
Javascript String Comparison
- 英文字母越後面越大,第一個字母相同則依序比較第二、第三...。
console.log("abandon" < "apple");
console.log("12" < "2");
結果為:都是 true
進階 Array Methods
arr.map(callbackFn)
- 創建一個新 array ,在 arr 中每個元素經過 callbackFn 執行後返回的值都會添加到此新的 array 裡面。
- arr.map(callbackFn) >>> A new array with each element being the result of the callback function.
範例:
let arr = [1, 2, 3, 4, 5, 6, 7];
let newArr = arr.map((i) => i ** 2);
console.log(newArr);
結果為:(return a new array)
[1, 4, 9, 16, 25, 36, 49]
- arr.forEach() >>> return undefine
範例:
let arr = [1, 2, 3, 4, 5, 6, 7];
let newArr = arr.forEach((i) => {
console.log(i ** 2);
});
console.log(newArr);
結果為:(return undefined)
1
4
9
16
25
36
49
undefined
範例 1:
let languages = ["Java", "C++", "Python", "Javascript"];
let lanResult = languages.map((lang) => lang.toUpperCase());
console.log(lanResult);
結果為:
[ 'JAVA', 'C++', 'PYTHON', 'JAVASCRIPT' ]
範例 2:
const codeLanguages = [
{ name: "Python", rating: 9.5, popularity: 9.7, trending: "super hot" },
{ name: "Java", rating: 9.4, popularity: 8.5, trending: "hot" },
{ name: "C++", rating: 9.2, popularity: 7.7, trending: "hot" },
{ name: "PHP", rating: 9.0, popularity: 5.7, trending: "decreasing" },
{ name: "JS", rating: 8.5, popularity: 8.7, trending: "hot" },
];
let codeResult = codeLanguages.map((lang) => {
return lang.name.toUpperCase();
});
console.log(codeResult);
結果為:
[ 'PYTHON', 'JAVA', 'C++', 'PHP', 'JS' ]
arr.find(callbackFn)
- 找到 arr 中第一個滿足 callbackFn 條件值 (return true) 的元素,並返回該元素,如果沒有值滿足 callbackFn 條件,則返回 undefined。
範例 :
const codeLanguages = [
{ name: "Python", rating: 9.5, popularity: 9.7, trending: "super hot" },
{ name: "Java", rating: 9.4, popularity: 8.5, trending: "hot" },
{ name: "C++", rating: 9.2, popularity: 7.7, trending: "hot" },
{ name: "PHP", rating: 9.0, popularity: 5.7, trending: "decreasing" },
{ name: "JS", rating: 8.5, popularity: 8.7, trending: "hot" },
];
let highPop = codeLanguages.find((lang) => {
return lang.popularity > 9.5;
});
console.log(highPop);
結果為:
{ name: 'Python', rating: 9.5, popularity: 9.7, trending: 'super hot' }
arr.filter(callbackFn)
- 過濾出所有滿足 callbacFn 中 return true 的元素。
範例 :
const codeLanguages = [
{ name: "Python", rating: 9.5, popularity: 9.7, trending: "super hot" },
{ name: "Java", rating: 9.4, popularity: 8.5, trending: "hot" },
{ name: "C++", rating: 9.2, popularity: 7.7, trending: "hot" },
{ name: "PHP", rating: 9.0, popularity: 5.7, trending: "decreasing" },
{ name: "JS", rating: 8.5, popularity: 8.7, trending: "hot" },
];
let rating = codeLanguages.filter((lang) => lang.rating >= 9.2);
console.log(rating);
結果為:
[
{
name: 'Python',
rating: 9.5,
popularity: 9.7,
trending: 'super hot'
},
{ name: 'Java', rating: 9.4, popularity: 8.5, trending: 'hot' },
{ name: 'C++', rating: 9.2, popularity: 7.7, trending: 'hot' }
]
arr.some(callbackFn)
- 測試 arr 中,是否至少有一個元素通過 callbackFn 測試並 return true (return type 為 boolean)。
範例 :
const codeLanguages = [
{ name: "Python", rating: 9.5, popularity: 9.7, trending: "super hot" },
{ name: "Java", rating: 9.4, popularity: 8.5, trending: "hot" },
{ name: "C++", rating: 9.2, popularity: 7.7, trending: "hot" },
{ name: "PHP", rating: 9.0, popularity: 5.7, trending: "decreasing" },
{ name: "JS", rating: 8.5, popularity: 8.7, trending: "hot" },
];
let lessPop = codeLanguages.some((lang) => lang.popularity <= 6);
console.log(lessPop );
結果為:true
arr.every(callbackFn)
- 測試 arr 中, 是否所有元素都通過 callbackFn 測試並return true (return type 為 boolean)。
範例 :
const codeLanguages = [
{ name: "Python", rating: 9.5, popularity: 9.7, trending: "super hot" },
{ name: "Java", rating: 9.4, popularity: 8.5, trending: "hot" },
{ name: "C++", rating: 9.2, popularity: 7.7, trending: "hot" },
{ name: "PHP", rating: 9.0, popularity: 5.7, trending: "decreasing" },
{ name: "JS", rating: 8.5, popularity: 8.7, trending: "hot" },
];
let allPop = codeLanguages.every((lang) => lang.popularity > 6);
console.log(allPop );
結果為:false
JS 內建排序函式
- 若想要把 array 內部的元素由小到大排序,可以用 JavaScript 內建的 sort() 方法。
- sort() 方法對 array 的元素進行就地排序,也就是說 array 會被永久改變。(絕大多數 JavaScript 內建的 method 並不會改變調用此 method 的變數的值,例如,String 的 toUpperCase() 就是一種)。
範例 1:
let Arrmy = [1, 5, 3, 2, 4, 7, 8, 0];
myarr.sort(); // array 會被永久改變
console.log(myArr);
結果為:[0, 1, 2, 3, 4, 5, 7, 8]
範例 2:
let myName = "Grace";
myName.toUpperCase(); // 不會改變調用此 method 的變數的值
console.log(myName);
結果為:Grace
myName = myName .toUpperCase(); // assign to 變數
結果為:GRACE
- 若希望保留未經過排序的 array,則需要先製作一個完整的複製品。
範例 :
let Arrmy = [1, 5, 3, 2, 4, 7, 8, 0];
let mySortedArr = [...Arrmy]; // 複製一個 array
lmySortedArr.sort(); // default sort (遞增)
console.log(Arrmy);
結果為:[1, 5, 3, 2, 4, 7, 8, 0]
console.log(mySortedArr);
結果為:[0, 1, 2, 3, 4, 5, 7, 8]
console.log(mySortedArr.reverse()); // 反轉
結果為:[8, 7, 5, 4, 3, 2, 1, 0]
- sort() 語法:
sort()
sort(compareFn)
- compareFn 是定義排序順序的函數。如果省略,則將 array 元素按照 JavaScript 預設方式排序(由小到大)。(數字: 1 < 2 < 3...;字母:a < b < c...)
範例 :
let fruits = ["Watermelon", "Apple", "Banana"];
fruits.sort();
console.log(fruits);
結果為:["Apple", "Banana", "Watermelon"]
- 若我們要根據自己提供的 compareFn 來排序,則此 compareFn 需要有兩個 parameter:a, b,sort() 會根據 compareFn 的 return value 來決定排序順序。若 return a - b,則採升序排序,若 return b - a,則採降序排序。其它 return 值為:

範例 1 :
let num = [9, 4, 3, 5, 6, 1, 0];
num.sort((a, b) => {
return b - a; // 降序排序
});
console.log(num);
結果為:[9, 6, 5, 4, 3, 1, 0]
範例 2 :
let fruits = ["Watermelon", "Apple", "Banana"];
fruits.sort((a, b) => {
if (a.length > b.length) {
return 1; // > 0, a after b; 所以字母較長的排在後面
} else {
return -1;
}
});
console.log(fruits);
結果為:['Apple', 'Banana', 'Watermelon']
- sort() 在使用時,如果沒有放入參數 compareFn,則 array 當中的每個元素將先被轉換為 String,再以 Unicode 編碼位置進行比較來排序。在 Unicode 編碼位置較後面者,會被排序在較後方。此 Unicode 編碼順序其實就是前面的 String comparison 影片中談過的字典順序。舉例來說,"banana" 會被排在 "cherry" 之前。同樣道理,在 Unicode 順序中 "1500" 會在 "2" 的前面,所以 1500 會被排在 2 前面。
範例 :
let myArr = [830, 1500, 1026, 48, 311, 122, 216, 93, 2];
myArr.sort();
console.log(myArr);
結果為:[1026, 122, 1500, 2, 216, 311, 48, 830, 93]
for of loop and for in loop
for of loop
- 創建一個迴圈,去循環可迭代對象 (iterable) 內的每個元素,可迭代對象:string, array, array-like object (例如:NodeList, HTMLCollection), TypedArray, Map, Set, user-defined。
- Object 並不是 iterable >> 不能用 for of loop
範例 1: for of loop
let numbers = [10, 20, 30];
for (let n of numbers) {
console.log(n);
}
結果為:10
20
30
比較:for loop
let numbers = [10, 20, 30];
for (let i = 0; i < numbers.length; i++) {
console.log(numbers[i]);
}
結果為:10
20
30
比較:forEach
let numbers = [10, 20, 30];
numbers.forEach((n) => console.log(n));
結果為:10
20
30
範例 2: for of loop - string
let myString = "Grace";
for (let i of myString) {
console.log(i);
}
結果為:G
r
a
c
e
for in loop
- 創建一個迴圈去循環一個 JS 物件中所有可枚舉屬性 (enumerable properties)
- 對 object 來說, enumerable properties 就是 keys
範例:
let Wilson = {
name: "Wilson Ren",
age: 26,
};
for (let property in Wilson) {
console.log(property);
// console.log(Wilson[property]);
}
結果為:name
age
// Wilson Ren
// 26
- 對 array 來說, enumerable properties 就是 indices
- 對 String 來說, enumerable properties 就是 indices
範例:
let inNumbers = [100, 44, 22];
for (let i in inNumbers) {
console.log(i); // index 的值
// console.log(inNumbers[i]);
}
結果為:0
1
2
// 100
// 44
// 22
Execution Context (執行環境)
- 當 JS 引擎執行程式碼 (script) 時,便會創建 execution contexts (執行環境)。JS 會建立兩種執行環境:
1. 全域執行環境 (Global Execution Context)
2. 函式執行環境 (Function Execution Context)
每種 execution context 都包含兩種階段:創造階段 (creation phase) 和執行階段 (execution phase)。
全域執行環境 (Global Execution Context)
- 當初次執行一份 JavaScript 程式碼時,JS 引擎會創造第一種 execution context,稱為全域執行環境 (Global Execution Context)。
- 在此 Global Execution Context 內部,會先進入創造階段 (creation phase):
a. 創建 global object (例如:瀏覽器中的 window object,或 Node.js 中的 global object)
b. 建立 scope
c. 創建 this 關鍵字,並綁訂至 global object
d. 將 variable、class 和 function 分配至記憶體 (RAM)。(hoisting 步驟)
- creation phase 結束後,會進入執行階段 (execution phase):
a. 逐行 (line by line) 執行程式碼
b. 遇到遞迴時,則使用 call stack 來排定工作順序
函式執行環境 (Function Execution Context)
- 每次的 function call,JS 引擎也會創造一個 function execution context,函式執行環境 (Function Execution Context) 和全域執行環境 (Global Execution Context) 非常類似,一樣也有創造階段 (creation phase) 和 執行階段 (execution phase)。
- 函式執行環境 (Function Execution Context) 不創建 global object,而是創建 argument object。此 argument object 包含了被放入此函式的 parameters 的數值參照值 (a reference to all the parameters passed into the function)。
- 函式執行環境 (Function Execution Context) 的創造階段 (creation phase):
a. 創建 argument object
b. 建立 scope (依照 closure 準則)
c. 創建 this 關鍵字
d. 將 variable、class 和 function 分配至記憶體 (RAM)。(hoisting 步驟)
- creation phase 結束後,會進入執行階段 (execution phase):
a. 逐行 (line by line) 執行程式碼
b. 遇到遞迴時,則使用 call stack 來排定工作順序

Hoisting (提升)
- 是指 JS 引擎在執行代碼之前,將 function、variables 或 class 的 declaration 移動到其範圍頂部的過程。
- 其優點之一是它允許我們在 code 中,declare function 之前就可以使用這個 function。(只對 function declaration 有用)
- Hoisting 也適用於 variables,因此可以在 declaration 和/或 initialization 之前在 code 中使用 variables。然而 JavaScript 只 hoist declaration,而不是 initialization!也就是說,let x = 10; 這段程式碼只有 let x 會被放到程式碼頂部。
- Hoisting 發生時,對於使用 var 做 declaration 的 variable 會給定初始值 undefined 並完成 initialization。然而,對於使用 let、const 做 declaration 的 variable 並不會給定任何初始值。
console.log(x);
var x;
結果:undefined
- let 可以 declare without initialization,且我們可以使用 console.log() 檢查 let 的變數值是 undefined,但這個 undefined 的 initialization 並不像 var 是發生在 creation phase 的 hoisting 階段發生的,而是在 execution phase 的階段。
let x; // declare without initialization
console.log(x);
結果:undefined
console.log(x);
let x;
結果:Cannot access 'x' before initialization
Scope and Closure
- Scope 是指在當前的 execution context 之中,變數的可訪問性 (accessibility) 為何?
let x = 10; // 全域變數 global variable
function hello() {
function hello2() {
console.log(x + 10);
}
hello2();
}
hello();
結果:20
function hello3() {
let a = 10; // local variable
}
JavaScript 的變數有以下幾種 Scope:
1. Global scope
- The default scope for all code running in the script.
let myName = "Wilson";
function sayHi() {
console.log(myName + "說你好");
function sayHi2() {
console.log(myName + "說你好");
}
sayHi2();
}
sayHi()
結果:Wilson說你好
Wilson說你好
2. Module scope
- The scope for code running in module mode.
3. Function scope
- The scope is created with a function.
function hello() {
let myName = "Wilson"; // Function Scope
}
console.log(myName);
結果:ReferenceError: myName is not defined
4. Block scope (用 let 或是 const 去宣告的變數)
- The scope created with a pair of curly braces (a block).
- 常見於 loop 和 if statement
if(true) {
let x = 10;
}
console.log(x);
結果:ReferenceError: x is not defined
Closure
- 在 function execution context 中,如果發現不在 function score 內部的函數,JavaScript 將轉到其它地方查找。Closure (閉包) 就是指這種將函數與其周圍的狀態或語詞環境結合在一起的組合。
- 在 JavaScript 中,每次 function execution context 都會在 creation phase 創建 closure。
- Closure 的規則是:
1. 從 Argument Object 以及 local variable 去尋找。
let c = 100;
function add(a, b) {
return a + b + c;
}
console.log(add(3, 4));
結果:107
let c = 100;
function add(a, b) {
let c = 5;
return a + b + c;
}
console.log(add(3, 4));
結果:12
2. 若從 1 處找不到,則從分配給函數的記憶體位置開始尋找。
let myName = "小華";
function sayHi() {
let myName = "小明";
console.log(myName + "說你好"); // 小明說你好
sayHi2(); // 小華說你好
}
function sayHi2() {
console.log(myName + "說你好");
}
sayHi();
結果:小明說你好
小華說你好
let myName = "小華";
function sayHi() {
let myName = "小明";
console.log(myName + "說你好"); // 小明說你好
sayHi2(); // 小明說你好
function sayHi2() {
console.log(myName + "說你好");
}
}
sayHi();
結果:小明說你好
小明說你好
3. 若在目前的 execution context 找不到,就繼續往外層、往全域一層一層的去找。
let myName = "小華";
function sayHi() {
console.log(myName + "說你好");
function sayHi2() {
console.log(myName + "說你好");
}
function sayHi3() {
console.log(myName + "說你好");
}
sayHi3(); // 小華說你好
}
sayHi(); // 小華說你好
結果:小華說你好
小華說你好
Call Stack and Recursion
Call Stack(堆疊)
- Call stack 是 JS 引擎追蹤本身在調用多個函數的程式碼中位置的機制 (資料結構的一種)。
- Call stack 可以包住我們知道 JS 引擎當前正在運行什麼函式以及該函式中調用那些函式等。
- Last-in-first-out (後進先出)

- 其機制為:
1. 當執行函式 f1 時,JS引擎將其添加到 Call stack 中,然後開始執行該函式。
2. 若該函式內部又調用其它函式 f2 時,則函式 f2 添加到 Call stack中,然後開始執行該函式。
3. 當 f2 執行完畢後,JS引擎將其從 Call stack 中取出,並且從 f1 停止的位置繼續執行。
範例:
function f1() {
console.log("開始執行f1。。。");
f2();
console.log("結束執行f1。。。");
}
function f2() {
console.log("開始執行f2。。。");
console.log("結束執行f2。。。");
}
f1();
結果:開始執行f1。。。
開始執行f2。。。
結束執行f2。。。
結束執行f1。。。
4. 如果 Call stack 堆疊過高,高出記憶體分配給 call stack 的最大空間,則會導致 stack overflow 的問題。 例如:Recursion 遞迴。

Recursion 遞迴
- 在數學上,遞迴關係 (recurrence relation) 是一種定義數列的方式:數列的每一項目定義為前面項的函數。例如:我們可以定義數列 S
1. A base case S(1) = 2
2. S(n) = 2 x S (n - 1) for n >= 2
所以,S 會是等比數列 2, 4, 8, 16, 32, ...
範例:
function s(n) {
if (n ==1) {
return 2;
}
return 2 * s(n - 1);
}
console.log(s(10));
結果:1024
範例:recursive
function addUpto(n) {
// 方法一:for loop
// 方法二:等差數列公式解
// 方法三:recursive
if (n ==1) {
return 1;
}
return n + addUpto(n - 1);
}
console.log(addUpto(10));
console.log(addUpto(100));
console.log(addUpto(1000));
結果:55
5050
500500
- 程式語言中,當一個函式內部,執行自己這個函式,這種情況就是遞迴演算法 (recursive algorithm),因此遞迴演算法 (recursive algorithm) 絕對會產生 call stack。
Constructor Function
- 在 JavaScript 當中,若 function 被調用時使用了 new 關鍵字,則會被當成 constructor function 來使用。
- 在 JavaScript 當中,constructor function 以大寫開頭
- constructor function 中的 this 關鍵字指的是這一個新製作的物件。
- 此新的物件會占用額外的記憶體 (reference data type),並自動 return
範例:
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHi = function () {
console.log(this.name + "說你好");
};
}
let wilson = new Person("Wilson Ren", 26);
let mike = new Person("Mike Huang", 26);
let grace = new Person("Grace Xie", 26);
grace.sayHi();
結果:Grace Xie說你好
- 透過使用 Constructor Function,我們可以大量製造 attributes 與 methods 相似的物件。
- Constructor Function 製作出的每個物件是獨立的,所以會單獨占用記憶體位置。

// reference data type
console.log(wilson.sayHi == mike.sayHi);
結果: false (比較記憶體的位置)
Inheritance and the Prototype Chain
- 在 JavaScript 中,每個物件都有一個 private attribute 叫做 __proto__。
- __proto__屬性存放的值是另一個物件。若物件 A 的 __proto__屬性的值是設定成另一個物件 B,則物件 A 就會繼承物件 B 的所有 attribute 和 methods。
範例:
let wilson = {
name: "Wison",
sayHi() {
console.log("你好");
},
};
let mike = {
__proto__: wilson,
};
console.log(mike.name); // 繼承 wilson 物件的屬性
mike.sayHi(); // 繼承 wilson 物件的 method
結果: Wilson
你好
- 每個 constructor function 都可以設定 prototype 屬性 (prototype 屬性本質上來說,就是一個 empty object)。
範例:
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHi = function () {
console.log(this.name + "說你好");
};
}
console.log(Person.prototype);
結果: {} (空的物件)
- 所有從 constructor function 製作出來的物件,其__proto__屬性內定義的 attributes and methods 都是自動指向 (繼承) constructor function 的 prototype 屬性內的 attributes and methods。 => Prototype Inheritance
範例:
let wilson = new Person("Wilson Ren", 26); // wilson.__proto__ => Person.prototype
let mike = new Person("Mike Huang", 26); // mike.__proto__ => Person.prototype
- constructor function A 製作的物件 obj,如果檢查 obj.__proto__ == A.prototype,結果為 true。因為 obj.__proto__ 以及 A.prototype 都是' reference data type,所以 true 代表兩者指向同個記憶體位置。
範例:
console.log(wilson.__proto__ == Person.prototype); // true
console.log(mike.__proto__ == Person.prototype); // true
- 若從 constructor function 製作出的每個物件都有相似的 methods,我們可以將 methods 全部移動到 constructor function 的 prototype 屬性內部,來節省記憶體(RAM)空間。 (Object- Oriented Programming)
範例:
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHi = function () {
console.log(this.name + "說你好");
};
}
Person.prototype.hello = function () {
console.log(this.name + "說哈囉!");
};
let wilson = new Person("Wilson Ren", 26);
let mike = new Person("Mike Huang", 26);
Person.prototype.type = "人類";
wilson.hello(); // Wilson Ren說哈囉!
mike.hello(); // Mike Huang說哈囉!
console.log(wilson.hello == mike.hello); // true => 指向同個記憶體位置
console.log(wilson.type); // 人類
console.log(mike.type); // 人類
- JS 內建的資料類型都有繼承其他的 prototype。 例如: [1, 2, 3] 這個 array 繼承了 Array Prototype,而 Array Prototype 又繼承自 Object Prototype。這種 Prototype 不斷往上連結的結果就叫做 Prototype Chain。
- JavaScript 中的所有物件最後的 Prototype Chain 都會連到一個叫做 "Object Prototype" 的地方。Object Prototype 是 Prototype Chain 的終點。
範例:array
let arr = [1, 2, 3];
arr.push(4); // 自動繼承了 array.prototype.push()
console.log(arr); // [ 1, 2, 3, 4 ]
範例:string
let myString = "this is my string..."; // primitive coercion
newString = myString.toUpperCase(); // 自動繼承了 string.prototype.toUpperCase()
console.log(newString); // THIS IS MY STRING...
Function Methods
- function 是一種特別的物件,所有 function 都有繼承 Object prototype
- Function.prototype 內有以下三個常用的 methods:
function.bind(obj)
- 將 function 的 this 關鍵字綁定給 obj
範例:
let Grace = {
name: "Grace",
age: 26,
};
function getAge() {
return this.age;
}
let newFunction = getAge.bind(Grace);
console.log(newFunction()); // 26
functtion.call(obj, arg1, /* ..., */ argN)
- 使用給定的 obj 當作 this 值來調用函示。 arg1, /* ..., */ argN 為 optional
functtion.apply(obj, argsArray)
- 和 call 相同,但 arguments 是使用 arguments array。
範例:
let Mike = {
name: "Mike",
age: 27,
};
function getInfo(country, height) {
console.log(this.name + "來自" + country + ", 身高為" + height + "cm");
return this.age;
}
getInfo.call(Mike, "台灣", 170); // Mike來自台灣, 身高為170cm
getInfo.apply(Mike, ["台灣", 170]); // Mike來自台灣, 身高為170cm
Prototype Inheritance in Constructors
- constructor function A 可以透過兩個設定來繼承另一個 constructor function B 的 prototype 物件屬性:
1. 在 constructor function A 的內部執行 B.call(this, arg1, ..., argsN),我們可以透過這段程式碼將 B 所設定的物件屬性套給 A 做使用。
範例:
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHi = function () {
console.log(this.name + "說你好");
};
function Student(name, age, major, grade) {
Person.call(this, name, age);
this.major = major;
this.grade = grade;
}
let mike = new Student("Mike Hung", 26, "Chemistry", 3.5);
console.log(mike.name); // Mike Hung
console.log(mike.major); // Chemistry
2. 設定 A.prototype = Object.create(B.prototype)。
- Object.create() 可以創建一個全新的物件,這樣一來,所有 B.prototype 內部的所有 attributes 和 methods 都可以套用給 A.prototype。
- 所有 A.prototype 新增或設定的額外 attributes 和 methods 都需寫在 "A.prototype = Object.create(B.prototype)" 這行程式碼的下方。
範例:
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHi = function () {
console.log(this.name + "說你好");
};
function Student(name, age, major, grade) {
Person.call(this, name, age);
this.major = major;
this.grade = grade;
}
Student.prototype = Object.create(Person.prototype); // 創建一個 Person() 全新的物件,並繼承了 Person 物件裡面的 sayHi()
Student.prototype.study = function () {
console.log(this.name + "正在努力讀" + this.major);
}; // 新增一個 function。所有 A.prototype 新增或設定的額外 attributes 和 methods 都需寫在 "A.prototype = Object.create(B.prototype)" 這行程式碼的下方。
let steve = new Student("Steve Wong", 23, "Food Science", 3);
steve.sayHi(); // Steve Wong說你好
steve.study(); // Steve Wong正在努力讀Food Science
Class
- Class 語法可以用來取代 constructor function。
- Class 語法是 JavaScript 基於現有的 prototype inheritance 的語法糖。(Class 語法與 constructor function 語法可以完全互換)
範例:
// constructior function
function Student(name, age, major) {
this.name = name;
this.age = age;
this.major = major;
}
Student.prototype.sayHi = function () {
console.log(this.name + "說你好");
};
// Class 語法
class Student {
constructor(name, age, major) {
this.name = name;
this.age = age;
this.major = major;
}
sayHi() {
console.log(this.name + "說你好");
}
}
- 若一個 constructor function 要繼承另一個 constructor function 的 prototype 物件,則可以使用 extends 關鍵字。
範例:
// constructior function
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHi = function () {
console.log(this.name + "說你好");
};
function Student(name, age, major, grade) {
Person.call(this, name, age);
this.major = major;
this.grade = grade;
}
Student.prototype = Object.create(Person.prototype); // 創建一個 Person() 全新的物件,並繼承了 Person 物件裡面的 sayHi()
Student.prototype.study = function () {
console.log(this.name + "正在努力讀" + this.major);
}; // 新增一個 function。所有 A.prototype 新增或設定的額外 attributes 和 methods 都需寫在 "A.prototype = Object.create(B.prototype)" 這行程式碼的下方。
let steve = new Student("Steve Wong", 23, "Food Science", 3);
steve.sayHi(); // Steve Wong說你好
steve.study(); // Steve Wong正在努力讀Food Science
// Class 語法
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHi() {
console.log(this.name + "說你好");
}
}
// super
class Student extends Person {
constructor(name, age, major, grade) {
super(name, age); // 代表 extends Person 的 constructor function
this.major = major;
this.grade = grade;
}
study() {
console.log(this.name + "正在努力讀" + this.major);
}
}
let mike = new Student("Mike Huang", 26, "Chemistry", 3.5);
mike.sayHi(); // Mike Huang說你好
mike.study(); // Mike Huang正在努力讀Chemistry
- Static 關鍵字在 Class (constructor function)上定義 attrubutes and 可以透過 Class 本身來訪問 static variable (attrubutes) 或是執行 static method。
- Static 關鍵字所設定出來的 attrubutes and methods 屬於 Class 本身,不屬於由 Class 製作出的物件。
- 本質上來說,Static 關鍵字就是將 attrubutes and methods 設定在 constructor function 這個物件上,而不是在 constructor.prototype 這個 constructor function 的屬性上。
- instance property, instance method => 由 Class 製作出的物件可以使用。
- JavaScript 當中的內建 Class (or constructor function) 有許多 static properties, static methods, instance properties and instance methods。
例如:Array.isArray() 就是 Array Class 的 static method,可以用來確認某個資料是否為 Array。(用 typeof 確認 Array 時,結果只能看到 Object)
例如:Math Class 內部所有的 properties and methods 都是 Static,所以可以使用 Math.E、Math.PI、Math.floor() 等功能。
範例:
// constructior function
function Student(name, age, major) {
this.name = name;
this.age = age;
this.major = major;
}
Student.exampleProperty = 10;
Student.exampleFunction = function () {
console.log("這是一個沒有特別功能的function");
}; // 此 method 直接設定在 function 裡面
Student.exampleFunction(); // 這是一個沒有特別功能的function
Student.prototype.sayHi = function () {
console.log(this.name + "說你好"); // 此 method 設定在 function 的 prototype 裡面
};
let mike = new Student("Mike Huang", 26, "Chemistry", 3.5);
mike.exampleFunction(); // 不可以使用此 method
mike.sayHi(); // Mike Huang說你好
// Class 語法
class Student {
static exampleProperty = 10; // static property
constructor(name, age, major) {
this.name = name; // instance property
this.age = age; // instance property
this.major = major; // instance property
}
// instance method (由 Class 製作出的物件)
sayHi() {
console.log(this.name + "說你好");
}
// static method
static exampleFunction() {
console.log("這是一個沒有特別功能的function");
}
}
let mike = new Student("Mike", 26, "chemistry");
mike.exampleFunction(); // 無法使用
Student.exampleFunction(); // 這是一個沒有特別功能的function
範例:用 Class 來做一個 Circle 物件 (計算圓的面積)
class Circle {
static allCircles = [];
constructor(radius) {
this.radius = radius;
Circle.allCircles.push(this);
}
getArea() {
return Math.PI * this.radius * this.radius;
}
getPerimeter() {
return 2 * Math.PI * this.radius;
}
// static
static getAreaFormula() {
return "圓面積公式為pi * r * r";
}
// static method
static getAllCirclesAreaTotal() {
let total = 0;
Circle.allCircles.forEach((circle) => {
total += circle.getArea();
});
return total;
}
}
let c1 = new Circle(5);
let c2 = new Circle(10);
let c3 = new Circle(15);
console.log(Circle.getAllCirclesAreaTotal()); // 1099.5574287564277
Ternary Operator, default parameters, backtick
Ternary Operator
- Ternary Operator 是 JavaScript 唯一用到三個運算元的運算子。
- 在一個條件之後會跟著一個問號 (?),如果條件是 truthy,則在冒號 (:) 前的表達式會被執行,如果條件是 falsy,則在冒號 (:) 後的表達式會被執行。
- 常被用來當作 if 表達式的簡潔寫法。
- 語法為:
condition ? expressionIfTRue : expressionIfFalse
範例 1:if statement
let age = 20;
let price;
if (age < 18) {
price = 50;
} else {
price = 150;
}
console.log(price); // 150
範例 1:Ternary Operator
let age = 20;
let price = age < 18 ? 50 : 150;
console.log(price); // 150
範例 2:if statement
let age = 20;
let price;
if (age < 18) {
price = 50;
} else if (age <60) {
price = 150;
} else {
price = 75;
}
console.log(price); // 150
範例 2:Ternary Operator
let age = 20;
let price = age < 18 ? 50 : age < 60 ? 150 : 75;
console.log(price); // 150
Default Parameters
- 當調用了 function 但沒有給定足夠數量的 arguments 時,parameter 會被設定成為 undefined。
- 在 function 設定 Default Parameters 可以讓 functions 有預設的初始化值。
範例 :
function multiply(a = 1, b = 1) {
return a * b;
}
console.log(multiply(10)); // 只給一個 parameter 時,會帶入 b 的預設值 1
console.log(multiply()); // 都不給 parameters 時,會帶入 a 和 b 的預設值 1
結果為:10 * 1 = 10
1 * 1 = 1
backtick (``)
let age = 26;
let address = "台灣";
// 用 "" 表示
let myName = "Wilson 的年齡是" + age + ".且來自於" + address;
// 用 ‵` 表示
let myName = `Wilson 的年齡是${age}且來自於${address}。`;
console.log(myName); // Wilson 的年齡是26且來自於台灣。
Destructuring Assignment
- 可以將 array 中的值或 object 中的屬性 unpack 到不同的變量中。常見的語法:
const [a, b] = array;
範例:
let arr = [1, 2, 3, 4, 5, 6, 7];
let [a1, a2, a3, a4, a5, a6, a7] = arr; // destructuring assignment
console.log("a1 is" + a1);
結果為: a1 is 1
const [a, b, ...rest] = array;
範例:
let arr = [100, 200, 300, 400, 500];
let [a1, a2, ...everything] = arr; // destructuring assignment
console.log(a1, a2, everything);
結果為: 100 200 [ 300, 400, 500 ]
const {a, b} = obj; (重要!!!)
範例:
let Wilson = {
name: "Wilson Ren",
age: 26,
address: "Hawaii",
height: 179,
weight: 75,
}
let { address } = Wilson; // destructuring assignment
console.log(address);
結果為: Hawaii
let { address, height, weight } = Wilson; // destructuring assignment
console.log(address, height, weight);
結果為: Hawaii 179 75
Switch Statement
- Switch Statement 是 if statement 的另一種選項,兩者的功能性完全相同。
- Switch Statement 會先獲得一個 expression,再將 expression 拿去跟一系列的 case 做比較。
- 在 Switch Statement 中,如果省略 break,則程式將繼續執行下一個 case,甚至執行到 default 子句,而不管該 case 的值是否匹配,此情形稱為 fall-through。
範例:if statement
let day = prompt("請輸入今天星期幾?");
if (day == "星期一") {
alert("Today is Monday!!");
} else if (day == "星期二") {
alert("Today is Tuesday!!");
} else if (day == "星期三") {
alert("Today is Wednesday!!");
} else if (day == "星期四") {
alert("Today is Thursday!!");
} else if (day == "星期五") {
alert("Today is Friday!!");
} else if (day == "星期六") {
alert("Today is Saturday!!");
} else if (day == "星期日") {
alert("Today is Sunday!!");
} else {
console.log("Cannot determine your day.");
}
範例:Switch statement
let day = prompt("請輸入今天星期幾?");
switch (day) {
case "星期一":
alert("Today is Monday!!");
break;
case "星期二":
alert("Today is Tuesday!!");
break;
case "星期三":
alert("Today is Wednesday!!");
break;
case "星期四":
alert("Today is Thursday!!");
break;
case "星期五":
alert("Today is Friday!!");
break;
case "星期六":
alert("Today is Saturday!!");
break;
case "星期日":
alert("Today is Sunday!!");
break;
default:
alert("Cannot determine your day.");
}
錯誤處理
- JavaScript 發生的錯誤會被自動做成一個 Error Object。
- JavaScript 中,如果要執行一段可能會出錯的程式碼,可以將該程式碼放入 try...catch... 語句當中。(常在後端中使用)
- 語法為:
try {
tryStatements
} catch (exceptionVar) {
catchStatements
} finally {
finallyStatements
}
tryStatements - 要執行的語句。
catchStatements - 如果在 try 中引發異常,則執行的語句。
exceptionVar (optional) - 一個變數,用於保存 catch 當中已捕獲的錯誤。
finallyStatements - 在完成 try...catch...語句時,一定會執行的語句。無論是否有發生異常,finallyStatements 都會執行。
- 可使用的語法:
try...catch...
try...finally...
try...catch...finally...
範例 1:
try {
whatever(); // 未定義完成的 function
} catch {
console.log("有錯誤");
}
結果為:有錯誤
範例 2:用變數 e 保存 catch 已捕獲的錯誤
try {
whatever; // 未定義完成的 function
} catch(e) {
console.log(e);
}
結果為:
ReferenceError: whatever is not defined...
instanceof operator (binary operator)
- 查看某個物件是否為某個 class 的 instance。
- object 和 instance 都是物件的意思。
範例 3:
class Person {
constructor(name) {
this.name = name;
}
}
let mike = new Person("Mike Hung");
console.log(mike instanceof Person);
結果為:true (mike 物件是 class Person 的 instance)
Error 種類查詢:Error - JavaScript - MDN
範例 4:區分 Error 種類
try {
whatever();
} catch (e) {
if (e instanceof TypeError) {
console.log("發生TypeError");
} else if (e instanceof ReferenceError) {
console.log("發生ReferenceError");
} else {
console.log("發生其它種類的Error");
}
}
結果為:
發生ReferenceError
範例 5:try...catch...finally...
try {
whatever();
} catch (e) {
if (e instanceof TypeError) {
console.log("發生TypeError");
} else if (e instanceof ReferenceError) {
console.log("發生ReferenceError");
} else {
console.log("發生其它種類的Error");
}
} finally {
console.log("不管有無錯誤,都會執行的程式碼");
}
結果為:
發生ReferenceError
不管有無錯誤,都會執行的程式碼
客製化錯誤訊息
- throw (丟), catch
範例 1:
// function 提供者在 function 內部寫入一個如果發生錯誤的錯誤訊息
function sumArray(arr) {
// Array Class static method
if (!Array.isArray(arr)) {
throw new TypeError("參數並非array!!");
}
let result = 0;
arr.forEach((element) => {
result += element;
});
return result;
}
// 使用者利用 try... catch... 查詢、接收錯誤信息
try {
console.log(sumArray([1, 2, 3, 4, 5]));
sumArray("hello");
} catch (e) {
console.log(e);
}
結果為:
15 // 正常執行,沒報錯
TypeError: 參數並非array!!...
範例 2:客製化錯誤訊息
// function 提供者在 function 內部寫入一個如果發生錯誤的客製化錯誤訊息
class NotArrayError extends TypeError {
constructor(message) {
super(message); // extends TypeError 的 constructor function
}
printSolution() {
return "請確定參數為 Array,再執行程式碼";
}
}
function sumArray(arr) {
// Array Class static method
if (!Array.isArray(arr)) {
throw new NotArrayError("參數並非array!!");
}
let result = 0;
arr.forEach((element) => {
result += element;
});
return result;
}
// 使用者利用 try... catch... 查詢、接收錯誤信息
try {
console.log(sumArray([1, 2, 3, 4, 5])); // 1
sumArray("hello");
} catch (e) {
console.log(e); // 2
console.log(e.printSolution); // 3
}
結果為:
1. 15 // 正常執行,沒報錯
2. NotArrayError: 參數並非array!!...
3. 請確定參數為 Array,再執行程式碼
thanks for your information