1. 類型檢查小工具
JavaScript 不是強(qiáng)類型語(yǔ)言,對(duì)此我推薦的最佳解決方案是 TypeScript。但有時(shí)你只是想要一個(gè)簡(jiǎn)單的類型檢查,這種時(shí)候 JavaScript 允許你使用“typeof”關(guān)鍵字。
“typeof”的問(wèn)題在于,將其用于某些原語(yǔ)和函數(shù)時(shí)效果很好,但對(duì)于數(shù)組和對(duì)象來(lái)說(shuō),由于它們都被視為“對(duì)象”,因此很難把握它們之間的區(qū)別。
const isOfType = (() => { // create a plain object with no prototype const type = Object.create(null); // check for null type type.null = x => x === null; // check for undefined type type.undefined = x => x === undefined; // check for nil type. Either null or undefined type.nil = x => type.null(x) || type.undefined(x); // check for strings and string literal type. e.g: 's', "s", `str`, new String() type.string = x => !type.nil(x) && (typeof x === 'string' || x instanceof String); // check for number or number literal type. e.g: 12, 30.5, new Number() type.number = x => !type.nil(x) && (// NaN & Infinity have typeof "number" and this excludes that (!isNaN(x) && isFinite(x) && typeof x === 'number' ) || x instanceof Number); // check for boolean or boolean literal type. e.g: true, false, new Boolean() type.boolean = x => !type.nil(x) && (typeof x === 'boolean' || x instanceof Boolean); // check for array type type.array = x => !type.nil(x) && Array.isArray(x); // check for object or object literal type. e.g: {}, new Object(), Object.create(null) type.object = x => ({}).toString.call(x) === '[object Object]'; // check for provided type instance type.type = (x, X) => !type.nil(x) && x instanceof X; // check for set type type.set = x => type.type(x, Set); // check for map type type.map = x => type.type(x, Map); // check for date type type.date = x => type.type(x, Date); return type;})();
2. 檢查是否為空
有時(shí)你需要知道某些內(nèi)容是否為空,并根據(jù)結(jié)果決定要使用的方法,例如檢查長(zhǎng)度、大小或是否包含任何子元素。下面這個(gè)工具打包了這些功能,你可以用它檢查 String、Object、Array、Map 和 Set 的大小。
function isEmpty(x) { if(Array.isArray(x) || typeof x === 'string' || x instanceof String ) { return x.length === 0; } if(x instanceof Map || x instanceof Set) { return x.size === 0; } if(({}).toString.call(x) === '[object Object]') { return Object.keys(x).length === 0; } return false;}
3. 獲取列表最后一項(xiàng)
其他語(yǔ)言里這個(gè)功能被做成了可以在數(shù)組上調(diào)用的方法或函數(shù),但在 JavaScript 里面,你得自己做點(diǎn)工作。
function lastItem(list) { if(Array.isArray(list)) { return list.slice(-1)[0]; } if(list instanceof Set) { return Array.from(list).slice(-1)[0]; } if(list instanceof Map) { return Array.from(list.values()).slice(-1)[0]; }}
4. 帶有范圍的隨機(jī)數(shù)生成器
有時(shí)你需要生成隨機(jī)數(shù),但希望這些數(shù)字在一定范圍內(nèi),那就可以用這個(gè)工具。
function randomNumber(max = 1, min = 0) { if(min >= max) { return max; } return Math.floor(Math.random() * (max - min) + min);}
5. 隨機(jī) ID 生成器
有時(shí)你只是需要一些 ID?除非你要的是更復(fù)雜的 ID 生成器(例如 UUID),否則用不著為此安裝什么新庫(kù),下面這個(gè)選項(xiàng)足夠了。你可以從當(dāng)前時(shí)間(以毫秒為單位)或特定的整數(shù)和增量開(kāi)始生成,也可以從字母生成 ID。
// create unique id starting from current time in milliseconds// incrementing it by 1 everytime requestedconst uniqueId = (() => { const id = (function*() { let mil = new Date().getTime(); while (true) yield mil += 1; })(); return () => id.next().value;})();// create unique incrementing id starting from provided value or zero// good for temporary things or things that id resetsconst uniqueIncrementingId = ((lastId = 0) => { const id = (function*() { let numb = lastId; while (true) yield numb += 1; })() return (length = 12) => `${id.next().value}`.padStart(length, '0');})();// create unique id from letters and numbersconst uniqueAlphaNumericId = (() => { const heyStack = '0123456789abcdefghijklmnopqrstuvwxyz'; const randomInt = () => Math.floor(Math.random() * Math.floor(heyStack.length)) return (length = 24) => Array.from({length}, () => heyStack[randomInt()]).join('');})();
6. 創(chuàng)建一個(gè)范圍內(nèi)的數(shù)字
Python 里我很喜歡的一個(gè)功能是 range 函數(shù),而在 JavaScript 里我經(jīng)常需要自己寫這個(gè)功能。下面是一個(gè)簡(jiǎn)單的實(shí)現(xiàn),非常適合 for…of 循環(huán)以及需要特定范圍內(nèi)數(shù)字的情況。
function range(maxOrStart, end = null, step = null) { if(!end) { return Array.from({length: maxOrStart}, (_, i) => i) } if(end <= maxOrStart) { return []; } if(step !== null) { return Array.from( {length: Math.ceil(((end - maxOrStart) / step))}, (_, i) => (i * step) + maxOrStart ); } return Array.from( {length: Math.ceil((end - maxOrStart))}, (_, i) => i + maxOrStart );}
7. 格式化 JSON 字符串,stringify 任何內(nèi)容
我在使用 NodeJs 將對(duì)象記錄到控制臺(tái)時(shí)經(jīng)常使用這種方法。JSON.stringify 方法需要第三個(gè)參數(shù),該參數(shù)必須有多個(gè)空格以縮進(jìn)行。第二個(gè)參數(shù)可以為 null,但你可以用它來(lái)處理 function、Set、Map、Symbol 之類 JSON.stringify 方法無(wú)法處理或完全忽略的內(nèi)容。

const stringify = (() => { const replacer = (key, val) => { if(typeof val === 'symbol') { return val.toString(); } if(val instanceof Set) { return Array.from(val); } if(val instanceof Map) { return Array.from(val.entries()); } if(typeof val === 'function') { return val.toString(); } return val; } return (obj, spaces = 0) => JSON.stringify(obj, replacer, spaces)})();
8. 順序執(zhí)行 promise
如果你有一堆異步或普通函數(shù)都返回 promise,要求你一個(gè)接一個(gè)地執(zhí)行,這個(gè)工具就會(huì)很有用。它會(huì)獲取函數(shù)或 promise 列表,并使用數(shù)組 reduce 方法按順序解析它們。
const asyncSequentializer = (() => { const toPromise = (x) => { if(x instanceof Promise) { // if promise just return it return x; } if(typeof x === 'function') { // if function is not async this will turn its result into a promise // if it is async this will await for the result return (async () => await x())(); } return Promise.resolve(x) } return (list) => { const results = []; return list .reduce((lastPromise, currentPromise) => { return lastPromise.then(res => { results.push(res); // collect the results return toPromise(currentPromise); }); }, toPromise(list.shift())) // collect the final result and return the array of results as resolved promise .then(res => Promise.resolve([...results, res])); }})();
9. 輪詢數(shù)據(jù)
如果你需要持續(xù)檢查數(shù)據(jù)更新,但系統(tǒng)中沒(méi)有 WebSocket,則可以使用這個(gè)工具來(lái)執(zhí)行操作。它非常適合上傳文件時(shí),想要持續(xù)檢查文件是否已完成處理的情況,或者使用第三方 API(例如 dropbox 或 uber)并且想要持續(xù)檢查過(guò)程是否完成或騎手是否到達(dá)目的地的情況。
async function poll(fn, validate, interval = 2500) { const resolver = async (resolve, reject) => { try { // catch any error thrown by the "fn" function const result = await fn(); // fn does not need to be asynchronous or return promise // call validator to see if the data is at the state to stop the polling const valid = validate(result); if (valid === true) { resolve(result); } else if (valid === false) { setTimeout(resolver, interval, resolve, reject); } // if validator returns anything other than "true" or "false" it stops polling } catch (e) { reject(e); } }; return new Promise(resolver);}
10. 等待所有 promise 完成
這個(gè)算不上是代碼解決方案,更多是對(duì) Promise API 的強(qiáng)化。這個(gè) API 在不斷進(jìn)化,以前我還為“allSettled”“race”和“any”做了代碼實(shí)現(xiàn),現(xiàn)在直接用 API 的就好了。

11. 交換數(shù)組值的位置
ES6 開(kāi)始,從數(shù)組中的不同位置交換值變得容易多了。這個(gè)做起來(lái)不難,但是了解一下也不錯(cuò),
12. 條件對(duì)象鍵
我最喜歡這條技巧了,我在使用 React 更新?tīng)顟B(tài)時(shí)經(jīng)常用它。你可以將條件包裝在括號(hào)中來(lái)有條件地將一個(gè)鍵插入一個(gè) spread 對(duì)象。
13. 使用變量作為對(duì)象鍵
當(dāng)你有一個(gè)字符串變量,并想將其用作對(duì)象中的鍵以設(shè)置一個(gè)值時(shí)可以用它。
14. 檢查對(duì)象里的鍵
這是一個(gè)很好的技巧,可以幫助你檢查對(duì)象鍵。

15. 刪除數(shù)組重復(fù)項(xiàng)
數(shù)組中經(jīng)常有重復(fù)的值,你可以使用 Set 數(shù)據(jù)結(jié)構(gòu)來(lái)消除它。它適用于許多數(shù)據(jù)類型,并且 set 有多種檢查相等性的方法,很好用。對(duì)于不同實(shí)例或?qū)ο蟮那闆r,你還是可以使用 Set 來(lái)跟蹤特定事物并過(guò)濾出重復(fù)的對(duì)象。

16. 在 ArrayforEach 中執(zhí)行“break”和“continue”
我真的很喜歡使用數(shù)組“.forEach”方法,但有時(shí)我需要提早退出或繼續(xù)進(jìn)行下一個(gè)循環(huán),而不想用 for...loop。你可以復(fù)制“continue”語(yǔ)句行為來(lái)提前返回,但如果要復(fù)制“break”行為,則需要使用數(shù)組“.some”方法。
17. 使用別名和默認(rèn)值來(lái)銷毀
Destructuring(銷毀)是 JavaScript 最好用的功能之一,而且你可以使用“冒號(hào)”設(shè)置別名,并使用“等號(hào)”設(shè)置屬性默認(rèn)值。

18. 可選鏈和空值合并
深入檢查對(duì)象屬性并處理 null 和 undefined 值時(shí),你可以使用幾個(gè)非常好用的 JavaScript 功能來(lái)解決常見(jiàn)的問(wèn)題。
19. 用函數(shù)擴(kuò)展類
我經(jīng)常對(duì)別人講,JavaScript 類只是構(gòu)造函數(shù)和底層的原型,不是像 Java 中那樣的真實(shí)概念。一個(gè)證據(jù)是,你可以只使用一個(gè)構(gòu)造函數(shù)來(lái)擴(kuò)展一個(gè)類。在私有內(nèi)容里這個(gè)很好用,在類里“#”這些看著很奇怪,并且用于 babel 或 WebPack 時(shí),編譯出來(lái)的代碼更少。
20. 擴(kuò)展構(gòu)造函數(shù)
類的一個(gè)問(wèn)題是你只能擴(kuò)展一個(gè)其他類。使用構(gòu)造函數(shù),你可以使用多個(gè)構(gòu)造函數(shù)來(lái)構(gòu)成一個(gè)函數(shù),這樣就會(huì)靈活多了。你可以使用函數(shù)原型的.apply 或.call 方法來(lái)實(shí)現(xiàn)。你甚至可以只擴(kuò)展函數(shù)的一部分,只要它是一個(gè)對(duì)象即可。
21. 循環(huán)任何內(nèi)容有時(shí),你需要循環(huán)任何可迭代的內(nèi)容(Set、Map、Object、Array、String 等)。這個(gè)非常簡(jiǎn)單的 forEach 函數(shù)工具就可以做到這一點(diǎn)。如果回調(diào)返回 true,它將退出循環(huán)。
function forEach(list, callback) { const entries = Object.entries(list); let i = 0; const len = entries.length; for(;i < len; i++) { const res = callback(entries[i][1], entries[i][0], list); if(res === true) break; }}
22. 使函數(shù)參數(shù)為 required
這是一種確保函數(shù)調(diào)用了完成工作所需內(nèi)容的絕佳方法。你可以使用默認(rèn)參數(shù)值的特性來(lái)調(diào)用函數(shù),然后就會(huì)拋出一個(gè)錯(cuò)誤。如果調(diào)用該函數(shù)時(shí)帶上了它需要的值,則該值將替換該函數(shù),并且什么也不會(huì)發(fā)生。使用 undefined 調(diào)用也有相同的效果。
function required(argName = 'param') { throw new Error(`"${argName}" is required`)}function iHaveRequiredOptions(arg1 = required('arg1'), arg2 = 10) { console.log(arg1, arg2)}iHaveRequiredOptions(); // throws "arg1" is requirediHaveRequiredOptions(12); // prints 12, 10iHaveRequiredOptions(12, 24); // prints 12, 24iHaveRequiredOptions(undefined, 24); // throws "arg1" is required
23. 創(chuàng)建模塊或單例
很多時(shí)候,你需要在加載時(shí)初始化某些內(nèi)容,設(shè)置它需要的各種事物,然后就可以在應(yīng)用程序中到處使用它,而無(wú)需再做什么補(bǔ)充工作。你可以使用 IIFE 函數(shù)來(lái)做到這一點(diǎn),這個(gè)函數(shù)太好用了。這種模塊模式用來(lái)隔離事物非常好用,它可以只暴露需要交互的內(nèi)容。
24. 深度克隆對(duì)象開(kāi)發(fā)人員通常會(huì)安裝一些類似“l(fā)odash”的庫(kù)來(lái)執(zhí)行這一操作,但使用純 JavaScript 來(lái)實(shí)現(xiàn)確實(shí)也很容易。這是一個(gè)簡(jiǎn)單的遞歸函數(shù):只要是一個(gè)對(duì)象,就使用函數(shù)的構(gòu)造器將其重新初始化為一個(gè)克隆,然后對(duì)所有屬性重復(fù)該過(guò)程。
const deepClone = obj => { let clone = obj; if (obj && typeof obj === "object") { clone = new obj.constructor(); Object.getOwnPropertyNames(obj).forEach( prop => (clone[prop] = deepClone(obj[prop])) ); } return clone;};
25. 深度凍結(jié)對(duì)象
如果你喜歡不變性,那么這個(gè)工具你一定要常備。
const deepClone = obj => { let clone = obj; if (obj && typeof obj === "object") { clone = new obj.constructor(); Object.getOwnPropertyNames(obj).forEach( prop => (clone[prop] = deepClone(obj[prop])) ); } return clone;};
JSON.stringify 替換參數(shù)的威力

1.轉(zhuǎn)換字符串所有字母大小寫
let str = 'adfdf DD ssFF'
let reverseCase = (str) => {
let res = [...str].map(item => {
return item === item.toLowerCase() ? item.toUpperCase() : item.toLowerCase()
}).join('')
return res
}
console.log(reverseCase(str)) //ADFDF dd SSff
2.字符串首字母轉(zhuǎn)化大寫
let str = 'i am shi niu bi ren'
let res = str.replace(/\b[a-z]/g, (str) => { return str.toUpperCase() })
console.log(res) //I Am Shi Niu Bi Ren
3.數(shù)組隨機(jī)排列
const arr = [2, 4, 5, 6, 1, 7, 8, 9, 0, 3]
arr.sort((a, b) => {
return Math.random() - 0.5
})
console.log(arr)
4.求數(shù)組的最大值
const arr = [2, 4, 5, 6, 1, 7, 8, 9, 0, 3]
let res = arr.reduce((pre, cur) => {
let res = cur > pre ? cur : pre
return res
})
console.log(res)//9
let res2 = Math.max(...arr)
console.log(res2)//9
5.求數(shù)組的隨機(jī)數(shù)
const arr = [2, 4, 5, 6, 1, 7, 8, 9, 0, 3]
let getRandomValue = (array) => {
return array[Math.floor(Math.random() * array.length)]
}
console.log(getRandomValue(arr))
6.判斷字符串是否對(duì)稱
let str1 = 'abcdcba'
let str2 = 'edcabaa'
let compare = (str) => {
return str.split('').reverse().join('') === str
}
console.log(compare(str2)) //false
7.判斷數(shù)組是否有重復(fù)項(xiàng)
const arr = [11, 2, 2, 33, 11, 88, 33, 99]
const arr1 = [11, 2, 33, 88, 99]
let repetition = (arr) => {
return new Set(arr).size !== arr.length
}
console.log(repetition(arr)) //true
8.統(tǒng)計(jì)數(shù)組元素出現(xiàn)的次數(shù)
let arr = ["a", "b", "c", "f", "b", "b", " b", "d", "d"]
let count = (arr) => {
let res = arr.reduce((preArr, curItem) => {
preArr[curItem] = preArr[curItem] ? preArr[curItem] + 1 : 1
return preArr
}, {})
return res
}
console.log(count(arr))//{ a: 1, b: 3, c: 1, f: 1, ' b': 1, d: 2 }
9.比較數(shù)組基本類型元素是否相同
let arr1 = [null, 1, 2]
let arr2 = [undefined, 1, 2]
console.log((arr1.length === arr2.length) && (arr1.every((item, index) => {
return item === arr2[index]
})))