原文基础上增加了其它方法以及注释等,进行了小幅度修改,便于阅读
注意箭头函数有无{}
会影响是否需要再return
原文地址
Adapter
适配器,以下大多利用闭包返回函数和...操作符(剩余操作符/扩展操作符)
promise化(promisify)
转换异步函数以返回一个promise。相当于node的util.promisify
使用currying返回一个函数,返回一个调用原始函数的Promise。使用... rest运算符传入所有参数。
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
可变参数函数转为数组参数函数
接受一个可变参数函数并返回一个闭包,该闭包接受一个参数数组映射到该函数的输入。 使用闭包和展开运算符(...)将参数数组映射到函数的输入。
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])
Array
数组最大值
Math.max()
和扩展操作符...
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 }
深度扁平(map)
利用Array.concat()
和...
扩展操作符以及递归
const deepFlatten = arr => {
return [].concat(...arr.map((v) =>{
return Array.isArray(v) ? deepFlatten(v) : v
}))
}
找出两个数组的不同项
利用set.has()
和filter
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))
}
扁平一层(reduce)
const flatten = arr => arr.reduce((a, v) => a.concat(v), [])
扁平等级(depth)
根据depth等级来扁平,默认为1
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))
}
挑选(pick)
从对象中挑选与给定键对应的键值对。
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
}
随机取数(sample)
同时也适用于字符串
const sample = arr =>{
return arr[Math.floor(Math.random()*arr.length)]
}
打乱(shuffle)
// 初级版
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]
联合(union)
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]]
Browser
页面底部是否可见
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;
};
获取url查询参数
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(视口)。
注意事项
- 必须在页面加载完成后才能运行,否则document对象还没生成,浏览器会报错
- clientWidth和clientHeight都是只读属性,不能对它们赋值。
- window.innerWidth是包括右边滚动条的宽度的
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}
Function
链式异步调用(chainAsync)
循环遍历包含异步事件的函数数组,每次异步事件完成后再调用
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))
})
}
/*
*/
链式调用promise
使用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
睡眠(sleep)
延迟异步函数的执行,延迟执行异步函数的一部分,通过把它放到睡眠状态,返回一个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.');
}
*/
Math
数组总和
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]
阶乘(factorial)
// 一个函数内处理的结果可以使用return 返回,这样在调用函数的地方就可以用变量接收返回
结果
const factorial = n =>{
// 不return的话那就不会返回这个数值 默认返回的是undefined
return n <= 1 ? 1 : n * factorial(n-1)
}
// factorial(6) -> 720
斐波那契数列(fibonacci)
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)
Media
语言转文字(读取文字)
了解有关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
Object
给定数组创建对象
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]])
对象深度选择(select)
可以避免深度对象选择不到时的报错?
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
String
首字母大写(capitalize)
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"
十六进制颜色转rgb
如果提供了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转hex颜色
将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'
UUID生成器
使用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'