Skip to main content

你沒有注意到的 js

|| 和 ?? 的比較

主要是差別在 0 和 false 是你想要取得的值的話,就會需要用 ?? 不然用 || 的話,會變成後面的值。

0 || 3; // 3
0 ?? 3; // 0
false || 3; // 3
false ?? 3; // false

至於其它的 null, undefined, 就都沒有差別了

null ?? 3; // 3
null || 3; // 3
undefined ?? 3; // 3
undefined || 3; // 3

For 的世界

for loop 就不寫了

for ... in

用於取得 object 的 key,所以只能對 object 作用

const obj = { a: 1, b: 2, c: 3 };

for (const prop in obj) {
console.log(prop);
}
// output: a, b, c

for ... of

用來取得 array 裡面的值

const obj = [{ a: 1 }, { b: 2 }, { c: 3 }];

for (const prop of obj) {
console.log(prop);
}
// output {a:1}, {b:2}, {c:3}

Object 的世界

entries

可以用 for of 的方式,直接取得 object 的 key 和 value

const object1 = {
a: "somestring",
b: 42,
};
console.log(Object.entries(object1));

for (const [key, value] of Object.entries(object1)) {
console.log(`${key}: ${value}`);
}

// expected output:
// [ ["a", "somestring"], ["b", 42] ]
// "a: somestring"
// "b: 42"

typeof vs instanceof

typeof

typeof 就是用來判斷參數是什麼型別,用法很簡單,就typeof A

回傳值基本上就是常見的 js data type:

"number", "string", "boolean", "object", "function", "undefined", "symbol", "bigint"

instanceof

instanceof 是用來判斷 A 是否為 B 的實例,比較的是原型(prototype)。用法:

object instanceof constructor; // object: The object to test.
// constructor:Function to test against

下面來舉個例子說明吧!

function C() {}
function D() {}

var o = new C();

// true, 因為C在o的原型鍊內 ; o.__proto__ === C.prototype
o instanceof C;

// false, 因為 D.prototype 不存在在 o 的原型鍊
o instanceof D; // true 因為object存在於o的原型鍊
// o.__proto__.__proto__ === Object.prototype
o instanceof Object;

參考文章

Map

Map 物件是簡單的 key-value 配對,物件(Object)Map 很相似,但是有以下幾點差別:

  1. Map 裡面的 key 是唯一的,如果 set 到重複的 key,則舊的 value 會被覆蓋。
  2. Map 中的 keys 會根據被添加資料的時間而有順序性,但 Object 沒有順序性。
  3. Object 的鍵(key)只能是 字串(Strings)Symbols,而 Map 的鍵可以是任何值,包含物件、函式或原始型別(primitive type)。
  4. 若要取得 Map 的大小非常容易,只需要取得 size 屬性即可;而 Object 的大小必須手動決定。
  5. 當需要經常增添刪減屬性時,使用 Map 的效能會比 Object 來得好。

ES6 中如果希望「陣列(Array)」的元素不會重複,可以使用 Set;如果是希望物件(Object)的鍵不會重複,則可以使用 Map

使用語法

// 建立 Map
let myMap = new Map(); // 建立空的 Map
let myMap = new Map([
[1, "one"],
[2, "two"],
]); // 建立 Map 時直接代入內容

let keyString = "a string",
keyObj = {},
keyFunc = function () {};

// 透過 .set(key, value) 來在 Map 中添加屬性
myMap.set(keyString, "value associated with string");
myMap.set(keyObj, "value associated with object");
myMap.set(keyFunc, "value associated with function");

// 方法
myMap.has(keyString); // true,透過 .has 判斷該 Map 中是否有某一屬性
myMap.size; // 3,透過 .size 來取得 Map 內的屬性數目
myMap.get(keyString); // 使用 .get(key) 可取得屬性的內容
myMap.delete(keyString); // 刪除 Map 中的某個屬性,成功刪除回傳 true,否則 false
myMap.clear(); // 清空整個 Map

Map 的疊代

myMap.keys(); // 取得 Map 的所有 keys,回傳 Iterable 的物件
myMap.values(); // 取得 Map 的所有 values,回傳 Iterable 的物件
myMap.entires(); // 取得 Map 的所有內容,回傳 Iterable 的物件
// 透過 for...of 可以列舉所有 Map 中的內容
for (let [key, value] of myMap) {
console.log(`The value of ${key} in Map is ${value}`);
}

for (let [key, value] of myMap.entries()) {
console.log(key + " = " + value);
}

myMap.forEach(function (value, key) {
console.log(key + " = " + value);
});

// 取得 Map 的 key
for (let key of myMap.keys()) {
console.log(key);
}

// 取得 Map 的所有 values
for (let value of myMap.values()) {
console.log(value);
}

Map 的複製(Clone)

let students = {
Aaron: { age: "29", country: "Taiwan" },
Jack: { age: "26", country: "USA" },
Johnson: { age: "32", country: "Korea" },
};

const studentMap = new Map(Object.entries(students));
const cloneMap = new Map(studentMap);

cloneMap.get("Aaron"); // { age: '29', country: 'Taiwan' }
studentMap === cloneMap; // false. Useful for shallow comparison

Map 的合併(Merge)

var first = new Map([
[1, "one"],
[2, "two"],
[3, "three"],
]);

var second = new Map([
[1, "uno"],
[2, "dos"],
]);

// 合併兩個 Map 時,後面的 Key 會覆蓋調前面的
// 透過 Spread operator 可以將 Map 轉換成陣列
var merged = new Map([...first, ...second]);

console.log(merged.get(1)); // uno
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three

Map 也可以和陣列相合併

// Merge maps with an array. The last repeated key wins.
var merged = new Map([...first, ...second, [1, "foo"]]);

console.log(merged.get(1)); // foo
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three

使用範例

使用 Object.entries 來將資料變成 iterable 代入 new Map 的參數中

let students = {
Aaron: { age: "29", country: "Taiwan" },
Jack: { age: "26", country: "USA" },
Johnson: { age: "32", country: "Korea" },
};

const studentMap = new Map(Object.entries(students));

// 透過 for...of 可以列舉所有 Map 中的內容
for (let [key, value] of studentMap) {
const { age, country } = value;
console.log(`${key} is ${age} year's old, and lives in ${country}`);
}

// 檢驗某個鍵是否存在
studentMap.has("Aaron");

console.log(studentMap.get("Jack")); // 取得 Map 的屬性內容,Object { age: '26', country: 'USA' }
studentMap.delete("Johnson"); // 刪除 Map 中的某個屬性
studentMap.clear(); // 清空整個 Map

console.log(studentMap.size); // 看看 Map 中有多少內容

補充

將 Map 轉成 Array

const theMap = new Map([
[1, "one"],
[2, "two"],
[3, "three"],
]);

const theArray = [...theMap.entries()]; // [[1, 'one'], [2, 'two'], [3, 'three']]
const arrayOfMapKeys = [...theMap.keys()]; // [1, 2, 3]
const arrayOfMapValues = [...theMap.values()]; // ["one", "two", "three"]

使用 Map 製作 HashTable

// Credits: Engine Lin (https://medium.com/@linengine)

// object 也可以使用
const arr = ["apple", "apple", "banana", "banana", "cat", "dog", "fat", "fat", "fat", "fat"];

const hashTable = new Map();

arr.forEach((item) => {
if (hashTable.has(item)) {
hashTable.set(item, hashTable.get(item) + 1);
} else {
hashTable.set(item, 1);
}
});

// Map { 'apple' => 2, 'banana' => 2, 'cat' => 1, 'dog' => 1, 'fat' => 4 }
console.log(hashTable);

參考文章

Set

ES6 中如果希望「陣列(Array)」的元素不會重複,可以使用 Set;如果是希望物件(Object)的鍵不會重複,則可以使用 Map

Set @ MDN

Set 的特色是有 has() 這個方法,可以快速判斷該 Set 中是否包含某個元素,重點不是讓我們把 Set 中的元素取出來用。

const obj = {
foo: "bar",
};
const mySet = new Set();
mySet.add(1); // [1]
mySet.add(5); // [1, 5]
mySet.add(5); // [1, 5],重複的元素不會被加進去,依然是
mySet.add(obj); // [ 1, 5, { foo: 'bar' } ]

mySet.has(5); // true
mySet.has(obj); // true
mySet.has({
foo: "bar",
}); // false

mySet.delete(5); // true,刪除成功
mySet.delete(2); // false,刪除失敗
mySet.size; // 2

// for ... of
mySet.entries(); // [key, value] 內容相同
mySet.values(); // 和 mySet.keys 得到相同的內容
mySet.keys(); // 和 mySet.values 得到相同的內容

mySet.forEach((item) => console.log("item", item));
mySet.clear();

基本使用

set 裡面的鍵不會重複,是 unique 的。

// new Set Type
let classroom = new Set(); // 建立教室這個 set
let Aaron = { name: "Aaron", country: "Taiwan" };
let Jack = { name: "Jack", country: "USA" };
let Johnson = { name: "Johnson", country: "Korea" };

// 把物件放入 set 中
classroom.add(Aaron);
classroom.add(Jack);
classroom.add(Johnson);

// 檢驗 set 中是否包含某物件
if (classroom.has(Aaron)) console.log("Aaron is in the classroom");

// 把物件移除 set 中
classroom.delete(Jack);
console.log(classroom.size); // 看看 set 中有多少元素
console.log(classroom);

陣列與集合間轉換

// Set 轉成 Array
let SetToArray = [...classroom]; // Array.from(classroom);

// Array 轉成 Set
let ArrayToSet = new Set(SetToArray);

過濾陣列中重複的元素

keywords: filter array, unique array element

利用 Set 中元素不會重複的特性,可以用來過濾掉陣列中重複的元素,留下唯一

const arr = [1, 1, 3, 5, 5, 7];
const arrToSet = new Set(arr); // Set { 1, 3, 5, 7 }
const uniqueArray = [...arrToSet]; // [ 1, 3, 5, 7 ]

例子

var mySet = new Set();

mySet.add(1); // Set { 1 }
mySet.add(5); // Set { 1, 5 }
mySet.add("some text"); // Set { 1, 5, 'some text' }
var o = { a: 1, b: 2 };
mySet.add(o); // Set { 1, 5, 'some text', { a: 1, b: 2 } }

// // o is referencing a different object so this is okay
mySet.add({ a: 1, b: 2 }); // Set { 1, 5, 'some text', { a: 1, b: 2 }, { a: 1, b: 2 } }

Promise

觀念

  • Promise 有三種狀態:pending, resolved/fulfilled, rejected
  • new Promise 內的函式會立即被執行,當 resolve 得到內容後,才會執行 .then
  1. .thenresolvedCallback 中,可以得到在 new Promise 中 resolve 內所得到的值(value)。
  2. 如果在 .thenresolvedCallbackreturn 一個值,則這個值會以 Promise 物件的形式傳到下一個 .then
  3. 因此在下一個 .thenresolvedCallback 中,可以取得上一個 .then 中 return 的值。
  4. 但如果我們在 .then 中是 return 另一個 new Promise ,則下一個 .then 會等到這個 Promise 中的 resolve 得到值後才執行。
  5. 且在下一個 .thenresolvedCallback 中,可以得到上一個 new Promiseresolve 的值
/**
* Promise 基本使用
* 在 new Promise 內的函式會被馬上執行,
* 當 resolve 得到內容後,才會執行 .then。
**/

const myPromise = new Promise((resolve, reject) => {
console.log("In new Promise, start callback"); // 立即執行
setTimeout(() => {
// 一秒後執行
let response = 10;
resolve(response);
}, 1000);
});

myPromise.then((value) => {
// 在 myPromise 被 resolve 時執行
console.log("The answer is " + value);
});

myPromise.catch((error) => {
// 在 myPromise 被 reject 時執行
console.log("error", error);
});

promise.all

Promise.all用於需要確認數個 Promise 全部被resolved後,才執行下個任務的這種情境。有任一失敗,就會進入失敗的處理。

而這數個 Promise,多半會被包在陣列中傳入Promise.all,語法如下:

const p1 = Promise.resolve(3);
const p2 = 1337;
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("foo");
}, 100);
});

Promise.all([p1, p2, p3]).then((values) => {
console.log(values); // [3, 1337, "foo"]
});

promise.race

Promise.racePromise.all的差別在,接在後面的then只會接收到一個值:最快實現/拒絕的那一個。只要有任一 Promise 有結果(不論是resolved或是rejected)就進入下一個任務,其餘的都忽略。

這個可以怎麼用呢?如果我的有一個 request 的打過去,我希望它在 10 秒內回來,如果沒有回來,我就去作其它的事情,是不是就可以用此招。

const nativeResult: any = await Promise.race([
FirebaseAuthentication.signInWithGoogle({ scopes: ["profile", "email"] }),
new Promise((_, reject) => setTimeout(() => reject(new Error("Sign-in timeout")), 15000)),
]);

如果我的登一直沒有回來,過了 15 秒我就要把它 reject 掉

參考文章[1]

刪除字串的最後一個字元

方法一 substring

const str = "DelftStacks";
const str2 = str.substring(0, str.length - 1);
console.log(str2);

方法二 slice

const str = "DelftStacks";
const str2 = str.slice(0, -1);
console.log(str2);

方法三 replace

const str = "DelftStacks";
const str2 = str.replace(/.$/, "");
console.log(str2);

Math

【學習筆記】JavaScript 浮點數計算:Math ceil()、floor()、round()、toFixed()

  1. Math.floor():無條件捨去,回傳大於所給數字的最小整數
  2. Math.ceil():無條件進位,回傳小於等於所給數字的最大整數
  3. Math.round():四捨五入
  4. toFixed():四捨六入五留雙

Math.floor():無條件捨去,回傳大於所給數字的最小整數

Math.floor(0.95); // 0
Math.floor(4); // 4
Math.floor(7.004); // 7
Math.floor(-0.95); // -1
Math.floor(-4); // -4
Math.floor(-7.004); // -8

Math.ceil():無條件進位,回傳小於等於所給數字的最大整數

Math.ceil(0.95); // 1
Math.ceil(4); // 4
Math.ceil(7.004); // 8
Math.ceil(-0.95); // -0
Math.ceil(-7.004); // -7

Math.round():四捨五入

Math.round(20.49); // 20
Math.round(20.5); // 21
Math.round(-20.5); // -20
Math.round(-20.51); // -21

toFixed():四捨六入五留雙

(1.23).toFixed(2); // 1.23
(1.234).toFixed(2); // 1.23
(1.235).toFixed(2); // 1.24

NumberFormat

轉化數字為有貨幣符號

Intl.NumberFormat.prototype.format()

const amount = 654321.987;

const options1 = { style: "currency", currency: "RUB" };
const numberFormat1 = new Intl.NumberFormat("ru-RU", options1);

console.log(numberFormat1.format(amount));
// Expected output: "654 321,99 ₽"

const options2 = { style: "currency", currency: "USD" };
const numberFormat2 = new Intl.NumberFormat("en-US", options2);

console.log(numberFormat2.format(amount));
// Expected output: "$654,321.99"

Array.prototype.flat()

可以攤平 array 裡面的 array , 相見恨晚~

const arr1 = [0, 1, 2, [3, 4]];

console.log(arr1.flat());
// expected output: Array [0, 1, 2, 3, 4]

const arr2 = [0, 1, [2, [3, [4, 5]]]];

console.log(arr2.flat());
// expected output: Array [0, 1, 2, Array [3, Array [4, 5]]]

console.log(arr2.flat(2));
// expected output: Array [0, 1, 2, 3, Array [4, 5]]

console.log(arr2.flat(Infinity));
// expected output: Array [0, 1, 2, 3, 4, 5]

數字前面補零

padStart()  會將用給定用於填充的字串,以重複的方式,插入到目標字串的起頭(左側),直到目標字串到達指定長度。

"abc".padStart(10); // "       abc"
"abc".padStart(10, "foo"); // "foofoofabc"
"abc".padStart(6, "123465"); // "123abc"