--- title: 你沒有注意到的 js --- ## || 和 ?? 的比較 主要是差別在 0 和 false 是你想要取得的值的話,就會需要用 `??` 不然用 `||` 的話,會變成後面的值。 ```javascript 0 || 3 // 3 0 ?? 3 // 0 false || 3 // 3 false ?? 3 // false ``` 至於其它的 null, undefined, 就都沒有差別了 ```javascript null ?? 3 // 3 null || 3 // 3 undefined ?? 3 // 3 undefined || 3 // 3 ``` ## For 的世界 ### ~~for loop 就不寫了~~ ### for ... in 用於取得 object 的 key,所以只能對 object 作用 ```javascript const obj = {a:1, b:2, c:3}; for (const prop in obj) { console.log(prop) } // output: a, b, c ``` ### for ... of 用來取得 array 裡面的值 ```javascript 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 ```jsx 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)。用法: ```javascript object instanceof constructor// object: The object to test. // constructor:Function to test against ``` 下面來舉個例子說明吧! ```javascript 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; ``` [參考文章](https://medium.com/@mengchiang000/js%E5%9F%BA%E6%9C%AC%E8%A7%80%E5%BF%B5-typeof-vs-instanceof-4dcb89e315df) ## 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`**。 使用語法 ```javascript // 建立 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 的疊代 ```javascript myMap.keys(); // 取得 Map 的所有 keys,回傳 Iterable 的物件 myMap.values(); // 取得 Map 的所有 values,回傳 Iterable 的物件 myMap.entires(); // 取得 Map 的所有內容,回傳 Iterable 的物件 ``` ```javascript // 透過 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) ```javascript 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) ```js 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 也可以和陣列相合併 ```js // 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` 的參數中 ```javascript 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 ```javascript 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 ```js // 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); ``` [參考文章](https://pjchender.dev/npm/npm-rx-js/#interval) ## Set ES6 中**如果希望「陣列(Array)」的元素不會重複,可以使用 `Set`;如果是希望物件(Object)的鍵不會重複,則可以使用 `Map`**。 > [Set](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Set) @ MDN Set 的特色是有 `has()` 這個方法,可以**快速判斷該 Set 中是否包含某個元素**,重點不是讓我們把 Set 中的元素取出來用。 ```js 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 的。 ```js // 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); ``` ### 陣列與集合間轉換 ```js // Set 轉成 Array let SetToArray = [...classroom]; // Array.from(classroom); // Array 轉成 Set let ArrayToSet = new Set(SetToArray); ``` ### 過濾陣列中重複的元素 ##### keywords: `filter array`, `unique array element` **利用 Set 中元素不會重複的特性,可以用來過濾掉陣列中重複的元素,留下唯一**: ```js 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 ] ``` ### 例子 ```js 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. 在`.then` 的 `resolvedCallback` 中,可以得到在 `new Promise` 中 resolve 內所得到的值(value)。 2. 如果在 `.then` 的 `resolvedCallback` 中 `return` 一個值,則這個值會以 Promise 物件的形式傳到下一個 `.then`。 3. 因此在下一個 `.then` 的 `resolvedCallback` 中,可以取得上一個 `.then` 中 return 的值。 4. 但如果我們在 `.then` 中是 return 另一個 `new Promise` ,則下一個 `.then` 會等到這個 Promise 中的 resolve 得到值後才執行。 5. 且在下一個 `.then` 的 `resolvedCallback` 中,可以得到上一個 `new Promise` 中 `resolve` 的值 ```javascript /** * 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`,語法如下: ```javascript 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.race`和`Promise.all`的差別在,**接在後面的**`then`**只會接收到一個值:最快實現/拒絕的那一個**。只要有任一 Promise 有結果(不論是`resolved`或是`rejected`)就進入下一個任務,其餘的都忽略。 [參考文章](https://medium.com/@yining1204/promise-all-%E4%BB%A5%E5%8F%8A-promise-race-fa167d5015fe) ## 刪除字串的最後一個字元 ### 方法一 substring ```javascript const str = "DelftStacks"; const str2 = str.substring(0, str.length - 1); console.log(str2); ``` ### 方法二 slice ```javascript const str = "DelftStacks"; const str2 = str.slice(0, -1); console.log(str2); ``` ### 方法三 replace ```javascript const str = "DelftStacks"; const str2= str.replace(/.$/, ''); console.log(str2); ```