after

指定一个函数在被调用多少次后执行

const after = (n, func) => {
  let count = 0;
  return (...args) => {
    count++;
    if (count >= n) {
      return func(...args);
    }
  };
};
​
const saves = ['profile', 'settings'];
​
const done = after(saves.length, () => {
  console.log('done saving!');
});
​
forEach(saves, type => {
  asyncSave({ type, complete: done });
});
// => Logs 'done saving!' after the two async saves have completed.

思路:返回一个函数,该函数在被调用 n 次后执行指定的函数。利用闭包记录当前已调用次数,判断是否达到执行条件。

ary:对指定函数进行封装,指定最多接收多少个参数

const ary =
  (func, n = func.length) =>
  (...args) =>
    func(...args.slice(0, n));
​
map(['6', '8', '10'], ary(parseInt, 1));
// => [6, 8, 10]

思路:对指定函数进行封装,指定最多接收 n 个参数。返回一个新函数,限制函数参数个数。

before:指定一个函数在被调用多少次前执行

const before = (n, func) => {
  let count = 0;
  return (...args) => {
    count++;
    if (count < n) {
      return func(...args);
    }
  };
};
​
jQuery(element).on('click', before(5, addContactToList));
// => Allows adding up to 4 contacts to the list.

思路:返回一个函数,该函数在被调用 n 次前执行指定的函数。利用闭包记录当前已调用次数,判断是否达到执行条件。

bind

绑定函数的 this 值和指定的参数,并返回一个新的函数

const bind =
  (func, thisArg, ...boundArgs) =>
  (...args) =>
    func.apply(thisArg, [...boundArgs, ...args]);
​
const greet = function (greeting, punctuation) {
  return `${greeting} ${this.user}${punctuation}`;
};
​
const object = { user: 'fred' };
​
var bound = bind(greet, object, 'hi');
bound('!');
// => 'hi fred!'
​
// Bound with placeholders.
var bound = bind(greet, object, _, '!');
bound('hi');
// => 'hi fred!'

思路:绑定函数的 this 值和指定的参数,并返回一个新的函数。利用 apply 和 bind 方法实现。

bindKey

与 bind 类似,但是绑定的是对象上的指定方法

const bindKey =
  (object, key, ...args) =>
  (...args2) =>
    object[key].apply(object, [...args, ...args2]);
​
const object = {
  user: 'fred',
  greet(greeting, punctuation) {
    return `${greeting} ${this.user}${punctuation}`;
  },
};
​
var bound = bindKey(object, 'greet', 'hi');
bound('!');
// => 'hi fred!'
​
object.greet = function (greeting, punctuation) {
  return `${greeting}ya ${this.user}${punctuation}`;
};
​
bound('!');
// => 'hiya fred!'
​
// Bound with placeholders.
var bound = bindKey(object, 'greet', _, '!');
bound('hi');
// => 'hiya fred!'

思路:与 bind 类似,但是绑定的是对象上的指定方法。利用 apply 和 bind 方法实现。

curry

对指定函数进行柯里化

const curry = (func, arity = func.length, ...args) =>
  arity <= args.length ? func(...args) : curry.bind(null, func, arity, ...args);
​
const abc = function (a, b, c) {
  return [a, b, c];
};
​
const curried = curry(abc);
​
curried(1)(2)(3);
// => [1, 2, 3]
​
curried(1, 2)(3);
// => [1, 2, 3]
​
curried(1, 2, 3);
// => [1, 2, 3]
​
// Curried with placeholders.
curried(1)(_, 3)(2);
// => [1, 2, 3]

思路:对指定函数进行柯里化。返回一个新函数,当参数数量不足时,继续返回一个新函数,直到参数数量足够执行原函数。

curryRight

与 curry 类似,但是从右到左处理参数

const curryRight = (func, arity = func.length, ...args) =>
  arity <= args.length
    ? func(...args.reverse())
    : curryRight.bind(null, func, arity, ...args);
​
const abc = function (a, b, c) {
  return [a, b, c];
};
​
const curried = curryRight(abc);
​
curried(3)(2)(1);
// => [1, 2, 3]
​
curried(2, 3)(1);
// => [1, 2, 3]
​
curried(1, 2, 3);
// => [1, 2, 3]
​
// Curried with placeholders.
curried(3)(1, _)(2);
// => [1, 2, 3]

思路:与 curry 类似,但是从右到左处理参数。

debounce

对指定函数进行防抖处理

const debounce = (func, wait, immediate = false) => {
  let timeoutId;
  return (...args) => {
    if (immediate && !timeoutId) {
      func(...args);
    }
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => {
      if (!immediate) {
        func(...args);
      }
      timeoutId = null;
    }, wait);
  };
};
​
// 避免窗口在变动时出现昂贵的计算开销。
jQuery(window).on('resize', debounce(calculateLayout, 150));
​
// 当点击时 `sendMail` 随后就被调用。
jQuery(element).on(
  'click',
  debounce(sendMail, 300, {
    leading: true,
    trailing: false,
  })
);
​
// 确保 `batchLog` 调用1次之后,1秒内会被触发。
const debounced = debounce(batchLog, 250, { maxWait: 1000 });
const source = new EventSource('/stream');
jQuery(source).on('message', debounced);
​
// 取消一个 trailing 的防抖动调用
jQuery(window).on('popstate', debounced.cancel);

思路:对指定函数进行防抖处理。返回一个新函数,在一段时间内只执行一次。

defer

将指定函数延迟执行

const defer = (func, ...args) => setTimeout(func, 1, ...args);
​
defer(text => {
  console.log(text);
}, 'deferred');
// => 一毫秒或更久一些输出 'deferred'。

思路:将指定函数延迟执行。利用 setTimeout 实现。

delay

将指定函数延迟一段时间后执行

const delay = (func, wait, ...args) => setTimeout(func, wait, ...args);
​
delay(
  text => {
    console.log(text);
  },
  1000,
  'later'
);
// => 一秒后输出 'later'。

思路:将指定函数延迟一段时间后执行。利用 setTimeout 实现。

flip

对指定函数的参数进行反转

const flip =
  fn =>
  (...args) =>
    fn(...args.reverse());
​
const flipped = flip(function () {
  return toArray(arguments);
});
​
flipped('a', 'b', 'c', 'd');
// => ['d', 'c', 'b', 'a']

思路:反转函数的参数顺序,返回一个新的函数

memoize

对指定函数进行记忆化处理,缓存函数的计算结果

const memoize = fn => {
  const cache = new Map();
  return (...args) => {
    const key = JSON.stringify(args);
    return cache.has(key)
      ? cache.get(key)
      : cache.set(key, fn(...args)).get(key);
  };
};
​
const object = { a: 1, b: 2 };
const other = { c: 3, d: 4 };
​
var values = memoize(values);
values(object);
// => [1, 2]
​
values(other);
// => [3, 4]
​
object.a = 2;
values(object);
// => [1, 2]
​
// 修改结果缓存。
values.cache.set(object, ['a', 'b']);
values(object);
// => ['a', 'b']
​
// 替换 `memoize.Cache`。
memoize.Cache = WeakMap;

思路:缓存函数的计算结果,返回一个新的函数

negate

对指定函数进行封装,返回原函数的否定值

const negate =
  fn =>
  (...args) =>
    !fn(...args);
​
function isEven(n) {
  return n % 2 == 0;
}
​
filter([1, 2, 3, 4, 5, 6], negate(isEven));
// => [1, 3, 5]

思路:返回一个新的函数,该函数执行原函数的结果取反

once

指定一个函数只能被调用一次

const once = fn => {
  let called = false;
  return (...args) => {
    if (!called) {
      called = true;
      return fn(...args);
    }
  };
};
​
const initialize = once(createApplication);
initialize();
initialize();
// `initialize` 只能调用 `createApplication` 一次。

思路:返回一个新的函数,该函数只能被调用一次

overArgs

对指定函数进行封装,转换参数的形式

const overArgs =
  (fn, transforms) =>
  (...args) =>
    fn(...args.map((arg, index) => transforms[index](arg)));
​
function doubled(n) {
  return n * 2;
}
​
function square(n) {
  return n * n;
}
​
const func = overArgs((x, y) => [x, y], [square, doubled]);
​
func(9, 3);
// => [81, 6]
​
func(10, 5);
// => [100, 10]

思路:返回一个新的函数,该函数对原函数的指定参数进行转换

partial

对指定函数进行部分应用,指定部分参数

const partial =
  (fn, ...args) =>
  (...newArgs) =>
    fn(...args, ...newArgs);
​
const greet = function (greeting, name) {
  return `${greeting} ${name}`;
};
​
const sayHelloTo = partial(greet, 'hello');
sayHelloTo('fred');
// => 'hello fred'
​
// 使用了占位符。
const greetFred = partial(greet, _, 'fred');
greetFred('hi');
// => 'hi fred'

思路:返回一个新的函数,该函数部分应用原函数的指定参数

partialRight

与 partial 类似,但是从右到左指定部分参数

const partialRight =
  (fn, ...args) =>
  (...newArgs) =>
    fn(...newArgs, ...args);
​
const greet = function (greeting, name) {
  return `${greeting} ${name}`;
};
​
const greetFred = partialRight(greet, 'fred');
greetFred('hi');
// => 'hi fred'
​
// 使用了占位符。
const sayHelloTo = partialRight(greet, 'hello', _);
sayHelloTo('fred');
// => 'hello fred'

思路:返回一个新的函数,该函数从右到左部分应用原函数的指定参数

rearg

对指定函数进行封装,调整参数的位置

const rearg =
  (fn, indexes) =>
  (...args) =>
    fn(...indexes.map(index => args[index]));
​
const rearged = rearg((a, b, c) => [a, b, c], [2, 0, 1]);
​
rearged('b', 'c', 'a');
// => ['a', 'b', 'c']

思路:返回一个新的函数,该函数调整原函数的参数顺序

rest

对指定函数进行封装,将参数集合成一个数组传入原函数

const rest =
  fn =>
  (...args) =>
    fn(args);
​
const say = rest(
  (what, names) =>
    `${what} ${initial(names).join(', ')}${size(names) > 1 ? ', & ' : ''}${last(
      names
    )}`
);
​
say('hello', 'fred', 'barney', 'pebbles');
// => 'hello fred, barney, & pebbles'

思路:返回一个新的函数,该函数将原函数的参数集合成一个数组传入

spread

对指定函数进行封装,将参数数组展开作为多个参数传入原函数

const spread = fn => args => fn(...args);
​
const say = spread((who, what) => `${who} says ${what}`);
​
say(['fred', 'hello']);
// => 'fred says hello'
​
const numbers = Promise.all([Promise.resolve(40), Promise.resolve(36)]);
​
numbers.then(spread((x, y) => x + y));
// => a Promise of 76

思路:返回一个新的函数,该函数将原函数的参数数组展开作为多个参数传入

throttle

对指定函数进行节流处理

const throttle = (fn, time) => {
  let timer;
  return (...args) => {
    if (!timer) {
      timer = setTimeout(() => {
        fn(...args);
        timer = null;
      }, time);
    }
  };
};

// 避免在滚动时过分的更新定位
jQuery(window).on('scroll', throttle(updatePosition, 100));

// 点击后就调用 `renewToken`,但5分钟内超过1次。
const throttled = throttle(renewToken, 300000, { trailing: false });
jQuery(element).on('click', throttled);

// 取消一个 trailing 的节流调用。
jQuery(window).on('popstate', throttled.cancel);

思路:返回一个新的函数,该函数对原函数进行节流处理