原文基礎上增長了其它方法以及註釋等,進行了小幅度修改,便於閱讀
注意箭頭函數有無{}
會影響是否須要再return
原文地址javascript
適配器,如下大多利用閉包返回函數和...操做符(剩餘操做符/擴展操做符)html
轉換異步函數以返回一個promise。至關於node的util.promisify
使用currying返回一個函數,返回一個調用原始函數的Promise。使用... rest運算符傳入全部參數。java
const promisify = func => { return (...args) =>{ return new Promise((resolve, reject) => { return func(...args,(err,result) =>{ return err ? reject(err) : resolve(result) }) }) } } // const delay = promisify((d, cb) => setTimeout(cb, d)) // delay(2000).then(() => console.log('Hi!')) -> Promise resolves after 2s
接受一個可變參數函數並返回一個閉包,該閉包接受一個參數數組映射到該函數的輸入。 使用閉包和展開運算符(...)將參數數組映射到函數的輸入。node
const spreadOver = fn =>{ return argsArr =>{ return fn(...argsArr) } } /* const arrayMax = spreadOver(Math.max) arrayMax([1,2,3]) // -> 3 arrayMax([1,2,4]) // -> 4 */ // 簡單點能夠 // Math.max(...[1,3,5])
Math.max()
和擴展操做符...
python
const arrayMax = arr => Math.max(...arr)
假值
const compact = arr => arr.filter(Boolean)
const countOccurrences = (arr, value) => { return arr.reduce((a,v) =>{ return v === value ? a + 1: a + 0 },0) }
const countedNames = (arr) => { return arr.reduce(function (accr, name) { if (name in accr) { accr[name]++; } else { accr[name] = 1; } return accr; }, {}); } // countedNames is: // { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }
利用Array.concat()
和...
擴展操做符以及遞歸git
const deepFlatten = arr => { return [].concat(...arr.map((v) =>{ return Array.isArray(v) ? deepFlatten(v) : v })) }
利用set.has()
和filter
github
const difference = (arr1, arr2) =>{ const s = new Set(arr2) return arr1.filter(x => !s.has(x)) }
const distinct = arr => [...new Set(arr)] const distinct = arr => [Array.from(new Set(arr))] const distinct = arr => { return arr.filter((i) => arr.indexOf(i) !== arr.lastIndexOf(i)) }
const filterNonUnique = arr =>{ return arr.filter((i) => arr.indexOf(i) === arr.lastIndexOf(i)) }
const flatten = arr => arr.reduce((a, v) => a.concat(v), [])
根據depth等級來扁平,默認爲1web
const flattenDepth = (arr, depth = 1) =>{ if (depth ===1) { return arr.reduce((a, v) => a.concat(v), []) } return arr.reduce((a, v) => { return a.concat(Array.isArray(v) ? flattenDepth(v, depth-1) : v) }, []) }
默認0到end正則表達式
const initialWithRange = (end, start = 0) =>{ return Array.from({length:end + 1 - start}).map((v, i) =>{ return i + start }) } const initialWithRange = (end, start = 0) =>{ return new Array(end + 1 - start).fill().map((v, i) =>{ return i + start }) }
const intersection = (arr1, arr2) =>{ const s = new Set(arr2) return arr1.filter((x) => s.has(x)) }
從對象中挑選與給定鍵對應的鍵值對。算法
const pick = (obj, arr) => arr.reduce((acc, curr) => (curr in obj && (acc[curr] = obj[curr]), acc), {}); const pick = (obj, arr) => { return arr.reduce((acc, curr) => { curr in obj && (acc[curr] = obj[curr]) return acc }, {}) } // 本質,遍歷檢查一遍 const pick = (obj, arr) => { var temp = {} arr.forEach((item, index) => { if (item in obj){ temp[item] = obj[item] } }) return temp }
同時也適用於字符串
const sample = arr =>{ return arr[Math.floor(Math.random()*arr.length)] }
// 初級版 const shuffle = arr => arr.sort(() => Math.random() - 0.5) // random order function shuffle(array) { var random = array.map(Math.random) return array.sort(function(a, b) { return random[a] - random[b] }) } // Fisher–Yates(費歇爾洗牌算法) function shuffle(arr) { for (let i = arr.length; i > 0; i--){ //下面一行的';'不可去掉,不然會報錯 let j = Math.floor(Math.random() *i); [arr[i-1], arr[j]] = [arr[j], arr[i-1]] } return arr }
const similarity = (arr1, arr2) =>{ return arr1.filter((item, i) => { return arr2.includes(item) }) } // similarity([1,2,3], [1,2,4]) -> [1,2]
const union = (arr1, arr2) => Array.from(new Set([...arr1, ...arr2])) // union([1,2,3], [4,3,2]) -> [1,2,3,4]
支持多參數傳入,利用...
剩餘操做符,不修改原數組
const without = (arr, ...args) =>{ return arr.filter((item) =>{ return !args.includes(item) }) } // without([2, 1, 2, 3], 1, 2) -> [3]
...rest
參數.根據原始數組中的位置進行分組,多個一維數組,按照原始位置進行合併爲二維數組,空缺的用undefined佔位
const zip = (...arrays) => { // 下面的...是擴展操做符,否則數組沒法傳入到Math.max() const maxLength = Math.max(...arrays.map((item) => item.length)) return Array.from({length:maxLength}).map((_, i) =>{ return Array.from({length: arrays.length},(_, j) =>{ return arrays[j][i] }) }) } //zip(['a', 'b'], [1, 2], [true, false]); -> [['a', 1, true], ['b', 2, false]] //zip(['a'], [1, 2], [true, false]); -> [['a', 1, true], [undefined, 2, false]]
const bottomVisible = () => { return document.documentElement.clientHeight + window.scrollY >= (document.documentElement.scrollHeight || document.documentElement.clientHeight) }
移動設備/桌面設備
const detectDeviceType = () => { return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ? "Mobile" : "Desktop"; } // detectDeviceType() -> "Desktop"
默認徹底可見,懶加載的時候會用到這個原理
const elementIsVisibleInViewport = (el, partiallyVisible = false) => { const { top, left, bottom, right } = el.getBoundingClientRect(); return partiallyVisible ? ((top > 0 && top < window.innerHeight) || (bottom > 0 && bottom < window.innerHeight)) && ((left > 0 && left < window.innerWidth) || (right > 0 && right < window.innerWidth)) : top >= 0 && left >= 0 && bottom <= window.innerHeight && right <= window.innerWidth; };
const getURLParameters = url => url.match(/([^?=&]+)(=([^&]*))/g).reduce( (a, v) => (a[v.slice(0, v.indexOf('='))] = v.slice(v.indexOf('=') + 1), a), {} );
const scrollToTop = () =>{ const distance = document.documentElement.scrollTop if(distance > 0){ window.requestAnimationFrame(scrollToTop) window.scrollTo(0, distance - distance/8) } }
一張網頁的所有面積,就是它的大小。一般狀況下,網頁的大小由內容和CSS樣式表決定。
瀏覽器窗口的大小,則是指在瀏覽器窗口中看到的那部分網頁面積,又叫作viewport(視口)。
注意事項
const getViewport = () =>{ return { width: document.documentElement.clientWidth, height: document.documentElement.clientHeight } }
若是網頁內容可以在瀏覽器窗口中所有顯示,不出現滾動條,那麼網頁的clientWidth和scrollWidth應該相等。可是實際上,不一樣瀏覽器有不一樣的處理,這兩個值未必相等。因此,咱們須要取它們之中較大的那個值
const getPageArea = () =>{ return { width: Math.max(document.documentElement.scrollWidth, document.documentElement.clientWidth), height: Math.max(document.documentElement.scrollHeight, document.documentElement.clientHeight) } }
指該元素的左上角相對於整張網頁左上角的座標。這個絕對位置要經過計算才能獲得。
不斷累加offsetParent的offsetTop和offsetLeft屬性
因爲在表格和iframe中,offsetParent對象未必等於父容器,因此上面的函數對於表格和iframe中的元素不適用。
function getElementLeft(element){ var actualLeft = element.offsetLeft; var current = element.offsetParent; while (current !== null){ actualLeft += current.offsetLeft; current = current.offsetParent; } return actualLeft; } function getElementTop(element){ var actualTop = element.offsetTop; var current = element.offsetParent; while (current !== null){ actualTop += current.offsetTop; current = current.offsetParent; } return actualTop; }
利用getBoundingClientRect
方法
此方法其中包含了left、right、top、bottom四個屬性,分別對應了該元素的左上角和右下角相對於瀏覽器窗口(viewport)左上角的距離.(其實也就是網頁元素的相對位置)
var X= this.getBoundingClientRect().left+document.documentElement.scrollLeft; var Y =this.getBoundingClientRect().top+document.documentElement.scrollTop;
網頁元素的相對位置,指該元素左上角相對於瀏覽器窗口左上角的座標。
有了絕對位置之後,得到相對位置就很容易了,只要將絕對座標減去頁面的滾動條滾動的距離就能夠了 也就是減去document.documentElement.scrollLeft|scrollTop
//快捷方法 var X= this.getBoundingClientRect().left; var Y =this.getBoundingClientRect().top;
返回當前頁面的滾動位置。請使用pageXOffset和pageYOffset, 若是已定義,不然使用scrollLeft和scrollTop。你能夠省略el來使用窗口的默認值。pageXOffset是scrollY的別名(event.pageX是鼠標活動事件的屬性)
const getScrollPosition = (el = window) =>{ return ({x: (el.pageXOffset !== undefined) ? el.pageXOffset : el.scrollLeft, y: (el.pageYOffset !== undefined) ? el.pageYOffset : el.scrollTop}); } // getScrollPosition() -> {x: 0, y: 200}
循環遍歷包含異步事件的函數數組,每次異步事件完成後再調用
const chainAsync = fns =>{ let curr = 0 const next = () =>{ fns[curr++](next) } next() } /* chainAsync([ next => { console.log('0 seconds'); setTimeout(next, 1000); }, next => { console.log('1 second'); setTimeout(next, 1000); }, next => { console.log('2 seconds'); } ]) */
執行從右到左的函數功能組合
使用Array.reduce()來執行從右到左的函數組合。最後(最右邊的)函數能夠接受一個或多個參數;其他的函數參數必須是一元的.
const compose = (...fns) => { return fns.reduce((f,g) => { //...args 剩餘參數, args是數組,再轉換爲函數參數 return (...args) => f(g(...args)) }) } /* const add5 = x => x + 5 const multiply = (x, y) => x * y const multiplyAndAdd5 = compose(add5, multiply) multiplyAndAdd5(5, 2) -> 15 */
執行從左到右的函數功能組合
使用Array.reduce()和spread運算符(...)來執行從左到右的函數組合。第一個(最左邊的)函數能夠接受一個或多個參數;其他的功能必須是一元的。
const pipeFunctions = (...fns) => { return fns.reduce((f,g) => { //...args 剩餘參數, args是數組,再轉換爲函數參數 return (...args) => g(f(...args)) }) } /* */
使用Array.reduce()建立一個promise鏈,每一個promise在解析後返回下一個promise
const promiseSeries = ps => { return ps.reduce((p, next) => { return p.then(next) }, Promise.resolve()) } // const delay = (d) => new Promise(r => setTimeout(r, d)) // runPromisesInSeries([() => delay(1000), () => delay(2000)]) -> executes each promise sequentially, taking a total of 3 seconds to complete
延遲異步函數的執行,延遲執行異步函數的一部分,經過把它放到睡眠狀態,返回一個Promise。
const sleep = ms => { return new Promise((resolve,reject) => { setTimeout(resolve, ms) }) } /* async function sleepyWork() { console.log('I\'m going to sleep for 1 second.'); await sleep(1000); console.log('I woke up after 1 second.'); } */
const arraySum = arr => { return arr.reduce((acc,curr) => { return acc + curr },0) }
const arrayAverage = arr => { return arr.reduce((acc,curr) => { return acc + curr },0) / arr.length }
const digitize = number =>{ return [...number.toString()] // return [...''+number] } // digitize(2334) -> [2, 3, 3, 4]
// 一個函數內處理的結果可使用return 返回,這樣在調用函數的地方就能夠用變量接收返回 結果 const factorial = n =>{ // 不return的話那就不會返回這個數值 默認返回的是undefined return n <= 1 ? 1 : n * factorial(n-1) } // factorial(6) -> 720
const fibonacci = n =>{ return Array(n).fill(0).reduce((acc, val, i) =>{ return acc.concat(i >1 ? acc[i -1] + acc[i -2] : i) }, []) } // fibonacci(5) -> [0,1,1,2,3]
const isPrime = num =>{ for (var i = 2; i < num; i++) { if (num % i ===0){ return false } } return num >= 2; } // isPrime(11) -> true // isPrime(12) -> false // isPrime(1) -> false
const palindrome = str =>{ const s = str.toLowerCase().replace(/[\w_]/g, '') return s === s.split("").reverse().join('') } // palindrome('taco cat') -> true
const randomIntegerRange = (min, max) =>{ return Math.floor(Math.random()* (max - min +1)) + min } //randomIntegerRange (1, 10) -> 5
const roundx = (n, decimals=0) => { return Number(`${Math.round(`${n}e${decimals}`)}e-${decimals}`) } // roundx(1.005, 2) -> 1.01 const roundx = (n, decimals=0) =>{ return n.toFixed(decimals) } // roundx(1.2, 3) -> "1.200"
const standardDeviation = (arr, usePopulation = false) => { const mean = arr.reduce((acc, val) => acc + val, 0) / arr.length; return Math.sqrt( arr.reduce((acc, val) => acc.concat(Math.pow(val - mean, 2)), []) .reduce((acc, val) => acc + val, 0) / (arr.length - (usePopulation ? 0 : 1)) ); }; // standardDeviation([10,2,38,23,38,23,21]) -> 13.284434142114991 (sample)
瞭解有關Web Speech API的SpeechSynthesisUtterance接口的更多信息。
const speechSynthesis = message => { const msg = new SpeechSynthesisUtterance(message); msg.voice = window.speechSynthesis.getVoices()[0]; window.speechSynthesis.speak(msg); }; // speechSynthesis('Hello, World') -> plays the message
const objectFromPairs = arr => { return arr.reduce((acc, val) =>{ acc[val[0]] = val[1] return acc // return (acc[val[0]] = val[1], acc) },{}) } // objectFromPairs([['a',1],['b',2]]) -> {a: 1, b: 2}
const objectToPairs = obj =>{ return Object.keys(obj).map((key) =>{ return [key, obj[key]] }) } // objectToPairs({a: 1, b: 2}) -> [['a',1],['b',2]])
能夠避免深度對象選擇不到時的報錯?
const select = (from, selector) =>{ return selector.split('.').reduce((prev,cur) =>{ return prev && prev[cur] },from) } // const obj = {selector: {to: {val: 'val to select'}}}; // select(obj, 'selector.to.val'); -> 'val to select'
const truthCheckCollection = (collection, key) =>{ return collection.every((obj => { return obj[key] })) } // truthCheckCollection([{"user": "Tinky-Winky", "sex": "male"}, {"user": "Dipsy", "sex": "male"}], "sex") -> true
const capitalize = ([first, ...rest]) =>{ return first.toUpperCase() + rest.join('') } // capitalize('myName') -> 'MyName'
const capitalizeEveryWord = str =>{ // 匹配的單詞 return str.replace(/\b[a-z]/g, char =>{ return char.toUpperCase() }) }
const countVowels = str => { // 匹配不到時返回null 因此用[]避免報錯 return (str.match(/[aeiou]/ig) || []).length }
轉義字符串以在正則表達式中使用。 使用replace()來轉義特殊字符。
const escapeRegExp = str =>{ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') }
將駝峯形式的字符串轉換爲指定字符分割的形式
const fromCamelCase = (str, separator = '_') =>{ return str.replace(/([a-z\d])([A-Z])/g, '$1' + separator + '$2').replace(/([A-Z]+)([A-Z][a-z\d]+)/g, '$1' + separator + '$2').toLowerCase() } // fromCamelCase('someDatabaseFieldName', ' ') -> 'some database field name' // fromCamelCase('someLabelThatNeedsToBeCamelized', '-') -> 'some-label-that-needs-to-be-camelized' // fromCamelCase('someJavascriptProperty', '_') -> 'some_javascript_property'
const reverseString = str =>{ return str.split('').reverse().join('') } // reverseString('foobar') -> 'raboof'
const sortCharactersInString = str =>{ return str.split('').sort((a, b) => { return a.localeCompare(b) }).join(''); } // sortCharactersInString('cabbage') -> 'aabbceg'
使用replace()去除下劃線,連字符和空格,並將單詞轉換爲camelcase。
const toCamelCase = str =>{ return str.replace(/^([A-Z])|[\s-_]+(\w)/g, (match, p1, p2, offset) =>{ return p2 ? p2.toUpperCase() : p1.toLowerCase() }) } // toCamelCase("some_database_field_name") -> 'someDatabaseFieldName' // toCamelCase("Some label that needs to be camelized") -> 'someLabelThatNeedsToBeCamelized' // toCamelCase("some-javascript-property") -> 'someJavascriptProperty' // toCamelCase("some-mixed_string with spaces_underscores-and-hyphens") -> 'someMixedStringWithSpacesUnderscoresAndHyphens'
使用String.split()與提供的模式(默認爲非alpha做爲正則表達式)來轉換爲字符串數組。使用Array.filter()刪除任何空字符串。
const words = (str, pattrern = /[^a-zA-Z-]+/) =>{ return str.split(pattrern).filter(Boolean) } // ["I", "love", "javaScript", ""] // words("I love javaScript!!") -> ["I", "love", "javaScript"] // ["python", "javaScript", "coffee"] // words("python, javaScript & coffee") -> ["python", "javaScript", "coffee"]
## Utility
### 擴展Hex(16進制顏色)
將3位數的顏色代碼擴展爲6位數的顏色代碼
const extendHex = shortHex => { return '#' + shortHex.slice(shortHex.startsWith('#') ? 1 : 0).split('').map( s => s+s).join("") }
const getType = v =>{ v === undefined ? 'undefined' : v === null ? 'null' : v.constructor.name.toLowerCase() } // getType(new Set([1,2,3])) -> "set"
若是提供了alpha值,則將顏色代碼轉換爲rgb()或rgba()字符串。 使用&(和)運算符,按位右移運算符和掩碼位將具備RGB值的十六進制顏色代碼(帶或不帶前綴#)轉換爲字符串。若是是3位數的顏色代碼,則先轉換爲6位數字版本。若是一個alpha值和6位十六進制一塊兒提供,則返回rgba()字符串。
const hexToRGB = hex => { let alpha = false, h = hex.slice(hex.startsWith('#') ? 1 : 0); if (h.length === 3) h = [...h].map(x => x + x).join(''); else if (h.length === 8) alpha = true; h = parseInt(h, 16); return 'rgb' + (alpha ? 'a' : '') + '(' + (h >>> (alpha ? 24 : 16)) + ', ' + ((h & (alpha ? 0x00ff0000 : 0x00ff00)) >>> (alpha ? 16 : 8)) + ', ' + ((h & (alpha ? 0x0000ff00 : 0x0000ff)) >>> (alpha ? 8 : 0)) + (alpha ? `, ${(h & 0x000000ff)}` : '') + ')'; }; // hexToRGB('#27ae60ff') -> 'rgba(39, 174, 96, 255)' // hexToRGB('27ae60') -> 'rgb(39, 174, 96)' // hexToRGB('#fff') -> 'rgb(255, 255, 255)'
### 隨機十六進制顏色
使用Math.random生成一個隨機的24位(6x4bits)十六進制數字。使用位移,而後使用toString(16)將其轉換爲十六進制字符串。
const randomHexColorCode = () =>{ return '#'+(Math.random()*0xFFFFFF<<0).toString(16); } // randomHexColorCode() -> "#e34155" // randomHexColorCode() -> "#fd73a6" // randomHexColorCode() -> "#4144c6"
將RGB的值轉換爲顏色代碼。 使用按位左移運算符(<<)和toString(16),而後padStart(6,「0」)將給定的RGB參數轉換爲十六進制字符串以得到6位十六進制值。
const RGBToHex = (r, g, b) => { return ((r << 16) + (g << 8) + b).toString(16).padStart(6, '0'); } // RGBToHex(255, 165, 1) -> 'ffa501'
使用crypto API生成符合RFC4122版本4的UUID。
const UUIDGenerator = () => ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16) ); // UUIDGenerator() -> '7982fcfe-5721-4632-bede-6000885be57d'