閉包 (Closure)

閉包範例與解釋

閉包是函式被建立時所在的環境,環境由任意的局域變數所組成,這些變數是由在閉包建立的時間點上存在於作用域裡的所有變數,特殊的環境變數--本來以為應該消失,可是卻依然存在的變數

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function addFn() {
let num = 10

function ClosureAddFn(newNum) {
num = num + newNum
return num
}
//函示內 return 函示就會變成 『閉包』
return ClosureAddFn
}
const useClosure = addFn()

console.log(useClosure(10)) //20
console.log(useClosure(10)) //30
console.log(useClosure(10)) //40
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function addFn() {
let num = 10

function ClosureAddFn(newNum) {
num = num + newNum
return num
}
//函示內 return 函示就會變成 『閉包』
return ClosureAddFn
}
const useClosure = addFn()
console.log(useClosure(10)) //20
console.log(useClosure(10)) //30
console.log(useClosure(10)) //40

const useClosure2 = addFn()
console.log(useClosure2(100)) //110
console.log(useClosure2(100)) //210

參考資料

  • 閉包(Closure)的細節:共用與非共用變數
  • 閉包(Closure)的細節(二):模擬私有變數
  • 閉包(Closure)的細節(三):閉包與記憶體洩漏
  • 閉包(Closure)的細節(四):閉包效能與prototype

共用與非共用變數

一般函式在執行完畢時,內部的區域變數會被釋放掉,但如果在執行時產生了閉包。那閉包會保留其所在巢狀函式內的變數。讓他不至於被系統釋放掉。現在有一個問題是,閉包保留下來的變數,會與其他閉包共用嗎?這其實取決於閉包產生的位置與時間點。

同一個呼叫期間內的閉包共享變數:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function closuretest(){
x=10;
function changex(cx){
x=cx;
}
function showx(){
console.log(x)
}
f1= changex;
f2= showx;
}
closuretest();
console.log(f2())//顯示10
console.log(f1(5))
console.log(f2())//顯示5

不同呼叫期間的閉包各自擁有變數

1
2
3
4
5
6
7
8
9
10
function closuretest(ox){
var x=ox;
return function(){
console.log(x)
}
}
var f1=closuretest(5);
var f2=closuretest(10);
console.log(f1());//顯示5
console.log(f2());//顯示10

閉包效能與prototype

閉包有效能上的影響,主要是反映在處理速度與記憶體的占用。
以下誤用的的寫法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function MyPlayer(name,race,sex) {
this.name = name.toString();
this.race = race.toString();
this.sex = sex.toString();
this.getName = function() {
return this.name;
};
this.getRace = function() {
return this.race;
};
this.getSex = function() {
return this.sex;
};
}

不同呼叫期間的閉包各自擁有變數

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function MyPlayer(name,race,sex) {
this.name = name.toString();
this.race = race.toString();
this.sex = sex.toString();
}
MyPlayer.prototype.getName = function() {
return this.name;
};
MyPlayer.prototype.getRace = function() {
return this.race;
};
MyPlayer.prototype.getSex = function() {
return this.sex;
};

利用原型可以讓物件共享方法,這比上面有效率的多。
這範例說明了prototype與閉包的使用時機,不過嚴格來說上面兩種做法都有點不切實際,因為其實變數本身就是公開屬性了,在為其增加公開方法其實沒必要。比較常的做法是用閉包來模擬並且存取私有變數。用prototype來實作通用方法以及存取通用的私有變數。

Array.of()

Array.of() 方法從任何可迭代物件傳回一個陣列。

Array.of() 方法從任意數量的參數建立一個新數組。

Array.of() 方法可以採用任何類型的參數。

1
2
3
let fruits = Array.of("Banana", "Orange", "Apple", "Mango");
console.log(fruits)
//(4) ['Banana', 'Orange', 'Apple', 'Mango']

參考資料
Array.of()

entries()

entries()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

const fruits = ["Banana", "Orange", "Apple", "Mango"];

// 建立條目
const list = fruits.entries();

// 列出條目
let text = "";
for (let x of list) {
console.log(x)
}
//(2) [0, 'Banana']
//(2) [1, 'Orange']
//(2) [2, 'Apple']
//(2) [3, 'Mango']

參考資料
entries()

fetch 串接

fetch基本介紹

Response(請求回應)對象
  • json()它返回一個承諾,該承諾以將正文文本解析為JSON.
  • text()USVString 它返回一個以(en-US) (文本)解析的承諾。
  • formData()它返回一個用對象解析的承諾FormData。
  • blob返回一個以 .resolve 解決的承諾Blob
  • arrayBuffer()它返回一個用 .resolve 解決的承諾ArrayBuffer
  • redirect()使用不同的 URL 創建新的響應。
  • clone()建立一份Response的複製品。
  • error() 返回與網絡錯誤關聯的新對象。
參考 MDN 文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const api='https://api-node-mongo.onrender.com/api/banners';
fetch(api,{ method: "GET",headers: {
"Content-Type": "application/json"
},})
.then((res) => {
// 當 fetch 成功後,會回傳 ReadableStream 物件,回傳 ReadableStream 物件,可使用 .json() 等等方法,取得對應資料。
return res.json();
})
.then((data) => {
console.log(data);
})
.catch((error) => {
console.log(error)
})

fetch 搭配 async/await 範例

  • 狀況一:需等 Ajax1 完成後,才執行 Ajax2
    這種狀況使用 async/await 或是 原生 Promise 鏈式寫法都能達成,這邊以 async/await 為範例:
1
2
3
4
5
6
7
8
9
10
11
12
13
async function useAjax() {
try {
const ajax1 = await fetch('https://api-node-mongo.onrender.com/api/banners')
const data1 = await ajax1.json()
console.log('data1', data1)
const ajax2 = await fetch('https://api-node-mongo.onrender.com/api/news')
const data2 = await ajax2.json();
console.log('data2', data2)
} catch (error) {
console.log('錯誤:', err)
}
}
useAjax()
  • 狀況二: 需等 Ajax1、Ajax2 完成後,才執行 Ajax3
    這種狀況其實繼續使用 async/await 來寫也可以達成,不過會變成: 執行 Ajax1 ⇒ Ajax1 完成 ⇒ 執行 Ajax2 ⇒ Ajax2 完成 ⇒ 執行 Ajax3
    這樣就會浪費較多時間再等待 Ajax 回傳,所以比較好的方法就是使用 Promise.all() 搭配 async/await ,或是單純 Promise.all() 加上 Promise 鏈式寫法,這邊以 Promise.all() 搭配 async/await 為範例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
async function useAjax() {
try {
const ajax1 = fetch('https://api-node-mongo.onrender.com/api/banners')
const ajax2 = fetch('https://api-node-mongo.onrender.com/api/news')
const [res1, res2] = await Promise.all([ajax1, ajax2])
const data1 = await res1.json()
const data2 = await res2.json()
console.log('data1', data1)
console.log('data2', data2)
const ajax3 = await fetch('https://api-node-mongo.onrender.com/api/products')
const data3 = await ajax3.json()
console.log('data3', data3)
} catch(error) {
console.log('錯誤:', err)
}
}
useAjax()
參考文獻

join()

join()

join() 方法以字串形式傳回數組。
join() 方法不會更改原始陣列。
可以指定任何分隔符號。 預設為逗號 (,)。

1
2
3
4
5
6
7
8
const fruits = ["Banana", "Orange", "Apple", "Mango"];
let text = fruits.join();
console.log(text);
//Banana,Orange,Apple,Mango
let result = Array.isArray(fruits);
console.log(result);
console.log(Array.isArray(text));
//false

參考資料
join()

1
2
3
4
const fruits = ["Banana", "Orange", "Apple", "Mango"];
let text = fruits.join(" and ");
console.log(text);
//Banana and Orange and Apple and Mango

join()

sort()

sort() 方法以字母順序對陣列的元素進行排序。

1
2
3
4
5
6
// Create an Array
const fruits = ["Banana", "Orange", "Apple", "Mango"];

// Sort the Array
fruits.sort();
console.log(fruits.sort())

用匿名函式當排序條件

如果希望完全依照自己給的條件排序,最好是給sort()一個帶有條件的匿名函式來當參數,這個匿名函數必須要有兩個參數,然後再依照這兩個參數比較回傳的值,來當排序依據。

sort()會依匿名函式的參數與回傳的值為精確的排序規則:

當回傳值為負數時,那麼前面的數放在前面
當回傳值為正整數,那麼後面的數在前面
當回傳值為零,保持不動。
這個函式會每次都先拿兩組陣列裡的元素來比較,當回傳值為正,會讓後面的數跑到前面,以上述的規則來移動元素,大家有沒覺得這種方式很眼熟,其實就是使用冒泡排序法來達到排序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 沒有給參數的預設排序
const arr = [5, 9, 1, 3, 2, 6];
arr.sort(); // [1, 2, 3, 5, 6, 9]

// 以匿名函式回參數做「升序」排序
arr.sort(function(a, b) {
return a - b; // a - b > 0
});
// [1, 2, 3, 5, 6, 9]

// 如果要反過來做「降序」排序
arr.sort(function(a, b) {
return b - a;
});
// [9, 6, 5, 3, 2, 1]

參考資料
sort()

try...catch 流程控制與例外處理

try…catch 流程控制與例外處理

  • try...catch可以使用在錯誤或是例外情形(expection),錯誤可能是亂呼叫不存在的變數or函式,但例外情形是可以自己定義的
  • throw new Error() 要放在try區塊裡面才會被catch接到,丟出例外的throw
  • new Error() JS內建提供錯誤處理的物件,會連接到catch的error.message
  • try...catch...finally
1
2
3
4
5
6
7
try {
// 欲執行的程式碼
} carch(error) {
// 當錯誤發生時,欲執行的程式碼
} finally {
//錯誤與否都會被執行的區塊
}