你應該瞭解的25個JS技巧

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"寫代碼的時候總有一些東西是會重複出現的,次數多了你就會想找找捷徑了。這類問題中有很大一部分解決起來甚至連庫都不用裝。下面就是我多年來收集的前25個捷徑和小技巧。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1. 類型檢查小工具"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"JavaScript不是強類型語言,對此我推薦的最佳解決方案是TypeScript。但有時你只是想要一個簡單的類型檢查,這種時候JavaScript允許你使用“typeof”關鍵字。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"“typeof”的問題在於,將其用於某些原語和函數時效果很好,但對於數組和對象來說,由於它們都被視爲“對象”,因此很難把握它們之間的區別。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"const isOfType = (() => {\n \/\/ create a plain object with no prototype\n const type = Object.create(null);\n \n \/\/ check for null type\n type.null = x => x === null;\n \/\/ check for undefined type\n type.undefined = x => x === undefined;\n \/\/ check for nil type. Either null or undefined\n type.nil = x => type.null(x) || type.undefined(x);\n \/\/ check for strings and string literal type. e.g: 's', \"s\", `str`, new String()\n type.string = x => !type.nil(x) && (typeof x === 'string' || x instanceof String);\n \/\/ check for number or number literal type. e.g: 12, 30.5, new Number()\n type.number = x => !type.nil(x) \n && (\/\/ NaN & Infinity have typeof \"number\" and this excludes that\n (!isNaN(x) && isFinite(x)\n && typeof x === 'number'\n ) || x instanceof Number);\n \/\/ check for boolean or boolean literal type. e.g: true, false, new Boolean()\n type.boolean = x => !type.nil(x) && (typeof x === 'boolean' || x instanceof Boolean);\n \/\/ check for array type\n type.array = x => !type.nil(x) && Array.isArray(x);\n \/\/ check for object or object literal type. e.g: {}, new Object(), Object.create(null)\n type.object = x => ({}).toString.call(x) === '[object Object]';\n \/\/ check for provided type instance\n type.type = (x, X) => !type.nil(x) && x instanceof X;\n \/\/ check for set type\n type.set = x => type.type(x, Set);\n \/\/ check for map type\n type.map = x => type.type(x, Map);\n \/\/ check for date type\n type.date = x => type.type(x, Date);\n \n return type;\n})();\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2. 檢查是否爲空"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有時你需要知道某些內容是否爲空,並根據結果決定要使用的方法,例如檢查長度、大小或是否包含任何子元素。下面這個工具打包了這些功能,你可以用它檢查String、Object、Array、Map和Set的大小。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"function isEmpty(x) {\n if(Array.isArray(x)\n || typeof x === 'string'\n || x instanceof String\n ) {\n return x.length === 0;\n }\n \n if(x instanceof Map || x instanceof Set) {\n return x.size === 0;\n }\n \n if(({}).toString.call(x) === '[object Object]') {\n return Object.keys(x).length === 0;\n }\n \n return false;\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"3. 獲取列表最後一項"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其他語言裏這個功能被做成了可以在數組上調用的方法或函數,但在JavaScript裏面,你得自己做點工作。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"function lastItem(list) {\n if(Array.isArray(list)) {\n return list.slice(-1)[0];\n }\n \n if(list instanceof Set) {\n return Array.from(list).slice(-1)[0];\n }\n \n if(list instanceof Map) {\n return Array.from(list.values()).slice(-1)[0];\n }\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"4. 帶有範圍的隨機數生成器"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有時你需要生成隨機數,但希望這些數字在一定範圍內,那就可以用這個工具。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"function randomNumber(max = 1, min = 0) {\n if(min >= max) {\n return max;\n }\n \n return Math.floor(Math.random() * (max - min) + min);\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"5. 隨機ID生成器"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有時你只是需要一些ID?除非你要的是更復雜的ID生成器(例如UUID),否則用不着爲此安裝什麼新庫,下面這個選項足夠了。你可以從當前時間(以毫秒爲單位)或特定的整數和增量開始生成,也可以從字母生成ID。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"\/\/ create unique id starting from current time in milliseconds\n\/\/ incrementing it by 1 everytime requested\nconst uniqueId = (() => {\n const id = (function*() {\n let mil = new Date().getTime();\n \n while (true)\n yield mil += 1;\n })();\n \n return () => id.next().value;\n})();\n\/\/ create unique incrementing id starting from provided value or zero\n\/\/ good for temporary things or things that id resets\nconst uniqueIncrementingId = ((lastId = 0) => {\n const id = (function*() {\n let numb = lastId;\n \n while (true)\n yield numb += 1;\n })()\n \n return (length = 12) => `${id.next().value}`.padStart(length, '0');\n})();\n\/\/ create unique id from letters and numbers\nconst uniqueAlphaNumericId = (() => {\n const heyStack = '0123456789abcdefghijklmnopqrstuvwxyz';\n const randomInt = () => Math.floor(Math.random() * Math.floor(heyStack.length))\n \n return (length = 24) => Array.from({length}, () => heyStack[randomInt()]).join('');\n})();\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"6. 創建一個範圍內的數字"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Python裏我很喜歡的一個功能是range函數,而在JavaScript裏我經常需要自己寫這個功能。下面是一個簡單的實現,非常適合for…of循環以及需要特定範圍內數字的情況。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"function range(maxOrStart, end = null, step = null) { \n if(!end) {\n return Array.from({length: maxOrStart}, (_, i) => i)\n }\n \n if(end <= maxOrStart) {\n return [];\n }\n \n if(step !== null) {\n return Array.from(\n {length: Math.ceil(((end - maxOrStart) \/ step))}, \n (_, i) => (i * step) + maxOrStart\n );\n }\n \n return Array.from(\n {length: Math.ceil((end - maxOrStart))}, \n (_, i) => i + maxOrStart\n );\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"7. 格式化JSON字符串,stringify任何內容"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我在使用NodeJs將對象記錄到控制檯時經常使用這種方法。JSON.stringify方法需要第三個參數,該參數必須有多個空格以縮進行。第二個參數可以爲null,但你可以用它來處理function、Set、Map、Symbol之類JSON.stringify方法無法處理或完全忽略的內容。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/b8\/b880edc90693afbd76a370a6bf93fbcb.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"const stringify = (() => {\n const replacer = (key, val) => {\n if(typeof val === 'symbol') {\n return val.toString();\n }\n if(val instanceof Set) {\n return Array.from(val);\n }\n if(val instanceof Map) {\n return Array.from(val.entries());\n }\n if(typeof val === 'function') {\n return val.toString();\n }\n return val;\n }\n \n return (obj, spaces = 0) => JSON.stringify(obj, replacer, spaces)\n})();\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"8. 順序執行promise"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你有一堆異步或普通函數都返回promise,要求你一個接一個地執行,這個工具就會很有用。它會獲取函數或promise列表,並使用數組reduce方法按順序解析它們。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"const asyncSequentializer = (() => {\n const toPromise = (x) => {\n if(x instanceof Promise) { \/\/ if promise just return it\n return x;\n }\n \n if(typeof x === 'function') {\n \/\/ if function is not async this will turn its result into a promise\n \/\/ if it is async this will await for the result\n return (async () => await x())();\n }\n \n return Promise.resolve(x)\n }\n \n return (list) => {\n const results = [];\n \n return list\n .reduce((lastPromise, currentPromise) => {\n return lastPromise.then(res => {\n results.push(res); \/\/ collect the results\n return toPromise(currentPromise);\n });\n }, toPromise(list.shift()))\n \/\/ collect the final result and return the array of results as resolved promise\n .then(res => Promise.resolve([...results, res]));\n }\n})();\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"9. 輪詢數據"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你需要持續檢查數據更新,但系統中沒有WebSocket,則可以使用這個工具來執行操作。它非常適合上傳文件時,想要持續檢查文件是否已完成處理的情況,或者使用第三方API(例如dropbox或uber)並且想要持續檢查過程是否完成或騎手是否到達目的地的情況。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"async function poll(fn, validate, interval = 2500) {\n const resolver = async (resolve, reject) => {\n try { \/\/ catch any error thrown by the \"fn\" function\n const result = await fn(); \/\/ fn does not need to be asynchronous or return promise\n \/\/ call validator to see if the data is at the state to stop the polling\n const valid = validate(result);\n if (valid === true) {\n resolve(result);\n } else if (valid === false) {\n setTimeout(resolver, interval, resolve, reject);\n } \/\/ if validator returns anything other than \"true\" or \"false\" it stops polling\n } catch (e) {\n reject(e);\n }\n };\n return new Promise(resolver);\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"10. 等待所有promise完成"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個算不上是代碼解決方案,更多是對Promise API的強化。這個API在不斷進化,以前我還爲“allSettled”“race”和“any”做了代碼實現,現在直接用API的就好了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/ee\/ee3f617331392986f5c462d92aeb3add.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"11. 交換數組值的位置"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ES6開始,從數組中的不同位置交換值變得容易多了。這個做起來不難,但是瞭解一下也不錯,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/73\/736cc22b0cf2e944e5c193077ae820d3.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"12. 條件對象鍵"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我最喜歡這條技巧了,我在使用React更新狀態時經常用它。你可以將條件包裝在括號中來有條件地將一個鍵插入一個spread對象。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/ee\/eea2f05d7f83a90fae4d374e46a72158.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"13. 使用變量作爲對象鍵"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當你有一個字符串變量,並想將其用作對象中的鍵以設置一個值時可以用它。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/0d\/0d8f577213f874748b0973077f539123.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"14. 檢查對象裏的鍵"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這是一個很好的技巧,可以幫助你檢查對象鍵。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/76\/761e6c5235fb0e4e357ba3cef818f19f.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"15. 刪除數組重複項"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"數組中經常有重複的值,你可以使用Set數據結構來消除它。它適用於許多數據類型,並且set有多種檢查相等性的方法,很好用。對於不同實例或對象的情況,你還是可以使用Set來跟蹤特定事物並過濾出重複的對象。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"16. 在ArrayforEach中執行“break”和“continue”"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我真的很喜歡使用數組“.forEach”方法,但有時我需要提早退出或繼續進行下一個循環,而不想用for...loop。你可以複製“continue”語句行爲來提前返回,但如果要複製“break”行爲,則需要使用數組“.some”方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/7e\/7ee4cd162e860dc4f6788c22cc20514e.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"17. 使用別名和默認值來銷燬"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Destructuring(銷燬)是JavaScript最好用的功能之一,而且你可以使用“冒號”設置別名,並使用“等號”設置屬性默認值。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/7b\/7be56a0829324f503f51940f70a51815.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"18. 可選鏈和空值合併"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"深入檢查對象屬性並處理null和undefined值時,你可以使用幾個非常好用的JavaScript功能來解決常見的問題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/c4\/c492cf5c63451459f2074671e2f5cb83.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"19. 用函數擴展類"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我經常對別人講,JavaScript類只是構造函數和底層的原型,不是像Java中那樣的真實概念。一個證據是,你可以只使用一個構造函數來擴展一個類。在私有內容裏這個很好用,在類裏“#”這些看着很奇怪,並且用於babel或WebPack時,編譯出來的代碼更少。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"20. 擴展構造函數"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"類的一個問題是你只能擴展一個其他類。使用構造函數,你可以使用多個構造函數來構成一個函數,這樣就會靈活多了。你可以使用函數原型的.apply或.call方法來實現。你甚至可以只擴展函數的一部分,只要它是一個對象即可。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/5c\/5cab6feab17c06b65ba73c6e40a5489c.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"21. 循環任何內容"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有時,你需要循環任何可迭代的內容(Set、Map、Object、Array、String等)。這個非常簡單的forEach函數工具就可以做到這一點。如果回調返回true,它將退出循環。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"function forEach(list, callback) {\n const entries = Object.entries(list);\n let i = 0;\n const len = entries.length;\n \n for(;i < len; i++) {\n const res = callback(entries[i][1], entries[i][0], list);\n \n if(res === true) break;\n }\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"22. 使函數參數爲required"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這是一種確保函數調用了完成工作所需內容的絕佳方法。你可以使用默認參數值的特性來調用函數,然後就會拋出一個錯誤。如果調用該函數時帶上了它需要的值,則該值將替換該函數,並且什麼也不會發生。使用undefined調用也有相同的效果。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"function required(argName = 'param') {\n throw new Error(`\"${argName}\" is required`)\n}\nfunction iHaveRequiredOptions(arg1 = required('arg1'), arg2 = 10) {\n console.log(arg1, arg2)\n}\niHaveRequiredOptions(); \/\/ throws \"arg1\" is required\niHaveRequiredOptions(12); \/\/ prints 12, 10\niHaveRequiredOptions(12, 24); \/\/ prints 12, 24\niHaveRequiredOptions(undefined, 24); \/\/ throws \"arg1\" is required\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"23. 創建模塊或單例"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"很多時候,你需要在加載時初始化某些內容,設置它需要的各種事物,然後就可以在應用程序中到處使用它,而無需再做什麼補充工作。你可以使用IIFE函數來做到這一點,這個函數太好用了。這種模塊模式用來隔離事物非常好用,它可以只暴露需要交互的內容。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/7e\/7e51e332beff39b939cfbe7523177933.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"24. 深度克隆對象"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"開發人員通常會安裝一些類似“lodash”的庫來執行這一操作,但使用純JavaScript來實現確實也很容易。這是一個簡單的遞歸函數:只要是一個對象,就使用函數的構造器將其重新初始化爲一個克隆,然後對所有屬性重複該過程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"const deepClone = obj => {\n let clone = obj;\n if (obj && typeof obj === \"object\") {\n clone = new obj.constructor();\n \n Object.getOwnPropertyNames(obj).forEach(\n prop => (clone[prop] = deepClone(obj[prop]))\n );\n }\n return clone;\n};\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"25. 深度凍結對象"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你喜歡不變性,那麼這個工具你一定要常備。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"const deepClone = obj => {\n let clone = obj;\n if (obj && typeof obj === \"object\") {\n clone = new obj.constructor();\n \n Object.getOwnPropertyNames(obj).forEach(\n prop => (clone[prop] = deepClone(obj[prop]))\n );\n }\n return clone;\n};\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"原文鏈接"},{"type":"text","text":":"},{"type":"link","attrs":{"href":"https:\/\/beforesemicolon.medium.com\/25-javascript-code-solutions-utility-tricks-you-need-to-know-about-3023f7ed993e","title":"","type":null},"content":[{"type":"text","text":"25 Javascript Code Solutions Utility Tricks You Need to Know About"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章