Function
attempt
尝试调用作为参数的函数,返回结果或捕获到的错误对象。
使用 try ... catch
块返回函数的结果或适当的错误。
const attempt = (fn, ...args) => { try { return fn(...args); } catch (e) { return e instanceof Error ? e : new Error(e); } };
var elements = attempt(function(selector) { return document.querySelectorAll(selector); }, '>_>'); if (elements instanceof Error) elements = []; // elements = []
bind
创建一个函数,该函数使用给定的上下文调用 fn
,最初的参数列表中添加任何额外提供的可选参数。
返回一个 function
,它使用 Function.prototype.apply()
将给定的 context
应用于 fn
。 使用 spread 操作符(...
) 将任何额外提供的可选参数添加到参数列表中,并传递给 fn
。
const bind = (fn, context, ...boundArgs) => (...args) => fn.apply(context, [...boundArgs, ...args]);
function greet(greeting, punctuation) { return greeting + ' ' + this.user + punctuation; } const freddy = { user: 'fred' }; const freddyBound = bind(greet, freddy); console.log(freddyBound('hi', '!')); // 'hi fred!'
bindKey
创建一个函数,该函数以对象的给定键调用方法,并可选地在参数的开头添加任何额外提供的参数。
const bindKey = (context, fn, ...boundArgs) => (...args) => context[fn].apply(context, [...boundArgs, ...args]);
const freddy = { user: 'fred', greet: function(greeting, punctuation) { return greeting + ' ' + this.user + punctuation; } }; const freddyBound = bindKey(freddy, 'greet'); console.log(freddyBound('hi', '!')); // 'hi fred!'
chainAsync
链式调用异步函数。
循环遍历包含异步事件的函数数组,每次异步事件完成后调用 next
。
const chainAsync = fns => { let curr = 0; const last = fns[fns.length - 1]; const next = () => { const fn = fns[curr++]; fn === last ? fn() : fn(next); }; next(); };
chainAsync([ next => { console.log('0 seconds'); setTimeout(next, 1000); }, next => { console.log('1 second'); setTimeout(next, 1000); }, () => { console.log('2 second'); } ]);
checkProp
给定一个断言(predicate)函数和一个 prop
字符串,这个柯里化(curried)函数将接收一个 object
对象,把对象访问的属性值,传递给断言函数,
调用 obj
上的 prop
,将它传递给提供的 predicate
函数,并返回一个布尔值。
const checkProp = (predicate, prop) => obj => !!predicate(obj[prop]);
const lengthIs4 = checkProp(l => l === 4, 'length'); lengthIs4([]); // false lengthIs4([1,2,3,4]); // true lengthIs4(new Set([1,2,3,4])); // false (Set uses Size, not length) const session = { user: {} }; const validUserSession = checkProps(u => u.active && !u.disabled, 'user'); validUserSession(session); // false session.user.active = true; validUserSession(session); // true const noLength(l => l === undefined, 'length'); noLength([]); // false noLength({}); // true noLength(new Set()); // true
compose - 函数式编程术语:函数组合
执行从右到左的函数组合。
使用 Array.prototype.reduce()
执行从右到左的函数组合。 最后一个(最右边的)函数可以接受一个或多个参数;其余的函数必须是接收一个参数的函数。
const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)));
const add5 = x => x + 5; const multiply = (x, y) => x * y; const multiplyAndAdd5 = compose( add5, multiply ); multiplyAndAdd5(5, 2); // 15
composeRight
执行从左到右的函数组合。
使用 Array.prototype.reduce()
执行从左到右的函数组合。 第一个(最左边的)函数可以接受一个或多个参数;其余的函数必须是接收一个参数的函数。
const composeRight = (...fns) => fns.reduce((f, g) => (...args) => g(f(...args)));
const add = (x, y) => x + y; const square = x => x * x; const addAndSquare = composeRight(add, square); addAndSquare(1, 2); // 9
converge
接受聚合函数和分支函数列表,并返回一个函数,该函数将参数传递给每个分支函数,分支函数的结果作为参数传递给聚合函数。
使用 Array.prototype.map()
和 function .prototype.apply()
使每个函数处理给定的参数。 调用 coverger
使用 spread 操作符('...')得到所有其他函数的结果。
const converge = (converger, fns) => (...args) => converger(...fns.map(fn => fn.apply(null, args)));
const average = converge((a, b) => a / b, [ arr => arr.reduce((a, v) => a + v, 0), arr => arr.length ]); average([1, 2, 3, 4, 5, 6, 7]); // 4
curry --- 函数式编程术语:柯里化
柯里化一个函数。
使用递归。 如果提供的参数 (args
) 是函数 fn
接受的参数数量,则调用传递的函数 fn
, 否则返回一个可接收剩余参数被柯里化后的函数 fn
。 如果你想柯里化一个接受可变参数数量的函数(可变参数的函数,例如 Math.min()
),你可以选择将参数个数传递给第二个参数 arity
。
const curry = (fn, arity = fn.length, ...args) => arity <= args.length ? fn(...args) : curry.bind(null, fn, arity, ...args);
curry(Math.pow)(2)(10); // 1024 curry(Math.min, 3)(10)(50)(2); // 2
debounce
Creates a debounced function that delays invoking the provided function until at least ms
milliseconds have elapsed since the last time it was invoked.
Each time the debounced function is invoked, clear the current pending timeout with clearTimeout()
and use setTimeout()
to create a new timeout that delays invoking the function until at least ms
milliseconds has elapsed. Use Function.prototype.apply()
to apply the this
context to the function and provide the necessary arguments. Omit the second argument, ms
, to set the timeout at a default of 0 ms.
const debounce = (fn, ms = 0) => { let timeoutId; return function(...args) { clearTimeout(timeoutId); timeoutId = setTimeout(() => fn.apply(this, args), ms); }; };
window.addEventListener( 'resize', debounce(() => { console.log(window.innerWidth); console.log(window.innerHeight); }, 250) ); // Will log the window dimensions at most every 250ms
defer
Defers invoking a function until the current call stack has cleared.
Use setTimeout()
with a timeout of 1ms to add a new event to the browser event queue and allow the rendering engine to complete its work. Use the spread (...
) operator to supply the function with an arbitrary number of arguments.
const defer = (fn, ...args) => setTimeout(fn, 1, ...args);
// Example A: defer(console.log, 'a'), console.log('b'); // logs 'b' then 'a' // Example B: document.querySelector('#someElement').innerHTML = 'Hello'; longRunningFunction(); // Browser will not update the HTML until this has finished defer(longRunningFunction); // Browser will update the HTML then run the function
delay
Invokes the provided function after wait
milliseconds.
Use setTimeout()
to delay execution of fn
. Use the spread (...
) operator to supply the function with an arbitrary number of arguments.
const delay = (fn, wait, ...args) => setTimeout(fn, wait, ...args);
delay( function(text) { console.log(text); }, 1000, 'later' ); // Logs 'later' after one second.
functionName
Logs the name of a function.
Use console.debug()
and the name
property of the passed method to log the method's name to the debug
channel of the console.
const functionName = fn => (console.debug(fn.name), fn);
functionName(Math.max); // max (logged in debug channel of console)
hz
Returns the number of times a function executed per second. hz
is the unit for hertz
, the unit of frequency defined as one cycle per second.
Use performance.now()
to get the difference in milliseconds before and after the iteration loop to calculate the time elapsed executing the function iterations
times. Return the number of cycles per second by converting milliseconds to seconds and dividing it by the time elapsed. Omit the second argument, iterations
, to use the default of 100 iterations.
const hz = (fn, iterations = 100) => { const before = performance.now(); for (let i = 0; i < iterations; i++) fn(); return (1000 * iterations) / (performance.now() - before); };
// 10,000 element array const numbers = Array(10000) .fill() .map((_, i) => i); // Test functions with the same goal: sum up the elements in the array const sumReduce = () => numbers.reduce((acc, n) => acc + n, 0); const sumForLoop = () => { let sum = 0; for (let i = 0; i < numbers.length; i++) sum += numbers[i]; return sum; }; // `sumForLoop` is nearly 10 times faster Math.round(hz(sumReduce)); // 572 Math.round(hz(sumForLoop)); // 4784
推荐资源- ES6:正确的部分
学习新的ES6 JavaScript语言特性,如箭头函数,解构,生成器和更多的编写更干净,更有效,可读的程序。
memoize
Returns the memoized (cached) function.
Create an empty cache by instantiating a new Map
object. Return a function which takes a single argument to be supplied to the memoized function by first checking if the function's output for that specific input value is already cached, or store and return it if not. The function
keyword must be used in order to allow the memoized function to have its this
context changed if necessary. Allow access to the cache
by setting it as a property on the returned function.
const memoize = fn => { const cache = new Map(); const cached = function(val) { return cache.has(val) ? cache.get(val) : cache.set(val, fn.call(this, val)) && cache.get(val); }; cached.cache = cache; return cached; };
// See the `anagrams` snippet. const anagramsCached = memoize(anagrams); anagramsCached('javascript'); // takes a long time anagramsCached('javascript'); // returns virtually instantly since it's now cached console.log(anagramsCached.cache); // The cached anagrams map
negate
Negates a predicate function.
Take a predicate function and apply the not operator (!
) to it with its arguments.
const negate = func => (...args) => !func(...args);
[1, 2, 3, 4, 5, 6].filter(negate(n => n % 2 === 0)); // [ 1, 3, 5 ]
once
Ensures a function is called only once.
Utilizing a closure, use a flag, called
, and set it to true
once the function is called for the first time, preventing it from being called again. In order to allow the function to have its this
context changed (such as in an event listener), the function
keyword must be used, and the supplied function must have the context applied. Allow the function to be supplied with an arbitrary number of arguments using the rest/spread (...
) operator.
const once = fn => { let called = false; return function(...args) { if (called) return; called = true; return fn.apply(this, args); }; };
const startApp = function(event) { console.log(this, event); // document.body, MouseEvent }; document.body.addEventListener('click', once(startApp)); // only runs `startApp` once upon click
partial
Creates a function that invokes fn
with partials
prepended to the arguments it receives.
Use the spread operator (...
) to prepend partials
to the list of arguments of fn
.
const partial = (fn, ...partials) => (...args) => fn(...partials, ...args);
const greet = (greeting, name) => greeting + ' ' + name + '!'; const greetHello = partial(greet, 'Hello'); greetHello('John'); // 'Hello John!'
partialRight
Creates a function that invokes fn
with partials
appended to the arguments it receives.
Use the spread operator (...
) to append partials
to the list of arguments of fn
.
const partialRight = (fn, ...partials) => (...args) => fn(...args, ...partials);
const greet = (greeting, name) => greeting + ' ' + name + '!'; const greetJohn = partialRight(greet, 'John'); greetJohn('Hello'); // 'Hello John!'
runPromisesInSeries
Runs an array of promises in series.
Use Array.prototype.reduce()
to create a promise chain, where each promise returns the next promise when resolved.
const runPromisesInSeries = ps => ps.reduce((p, next) => 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
Delays the execution of an asynchronous function.
Delay executing part of an async
function, by putting it to sleep, returning a Promise
.
const sleep = ms => new Promise(resolve => 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.'); }
throttle
Creates a throttled function that only invokes the provided function at most once per every wait
milliseconds
Use setTimeout()
and clearTimeout()
to throttle the given method, fn
. Use Function.prototype.apply()
to apply the this
context to the function and provide the necessary arguments
. Use Date.now()
to keep track of the last time the throttled function was invoked. Omit the second argument, wait
, to set the timeout at a default of 0 ms.
const throttle = (fn, wait) => { let inThrottle, lastFn, lastTime; return function() { const context = this, args = arguments; if (!inThrottle) { fn.apply(context, args); lastTime = Date.now(); inThrottle = true; } else { clearTimeout(lastFn); lastFn = setTimeout(function() { if (Date.now() - lastTime >= wait) { fn.apply(context, args); lastTime = Date.now(); } }, Math.max(wait - (Date.now() - lastTime), 0)); } }; };
window.addEventListener( 'resize', throttle(function(evt) { console.log(window.innerWidth); console.log(window.innerHeight); }, 250) ); // Will log the window dimensions at most every 250ms
times
Iterates over a callback n
times
Use Function.call()
to call fn
n
times or until it returns false
. Omit the last argument, context
, to use an undefined
object (or the global object in non-strict mode).
const times = (n, fn, context = undefined) => { let i = 0; while (fn.call(context, i) !== false && ++i < n) {} };
var output = ''; times(5, i => (output += i)); console.log(output); // 01234
uncurry
Uncurries a function up to depth n
.
Return a variadic function. Use Array.prototype.reduce()
on the provided arguments to call each subsequent curry level of the function. If the length
of the provided arguments is less than n
throw an error. Otherwise, call fn
with the proper amount of arguments, using Array.prototype.slice(0, n)
. Omit the second argument, n
, to uncurry up to depth 1
.
const uncurry = (fn, n = 1) => (...args) => { const next = acc => args => args.reduce((x, y) => x(y), acc); if (n > args.length) throw new RangeError('Arguments too few!'); return next(fn)(args.slice(0, n)); };
const add = x => y => z => x + y + z; const uncurriedAdd = uncurry(add, 3); uncurriedAdd(1, 2, 3); // 6
unfold
Builds an array, using an iterator function and an initial seed value.
Use a while
loop and Array.prototype.push()
to call the function repeatedly until it returns false
. The iterator function accepts one argument (seed
) and must always return an array with two elements ([value
, nextSeed
]) or false
to terminate.
const unfold = (fn, seed) => { let result = [], val = [null, seed]; while ((val = fn(val[1]))) result.push(val[0]); return result; };
var f = n => (n > 50 ? false : [-n, n + 10]); unfold(f, 10); // [-10, -20, -30, -40, -50]
when
Tests a value, x
, against a predicate function. If true
, return fn(x)
. Else, return x
.
Return a function expecting a single value, x
, that returns the appropriate value based on pred
.
const when = (pred, whenTrue) => x => (pred(x) ? whenTrue(x) : x);
const doubleEvenNumbers = when(x => x % 2 === 0, x => x * 2); doubleEvenNumbers(2); // 4 doubleEvenNumbers(1); // 1