function.js

/** @module Function */

/**
 * 将函数fn转为一次函数。返回函数,函数只能执行一次。
 * @function noce
 * @param {function} fn - 执行的函数。
 * @return {function}
 * @example
 * const fn = once(() => '5')
 * console.log([fn(), fn()])
 * // => ['5', undefined]
 */
export const once = fn => {
  let called = false
  return function (...args) {
    if (called) return
    called = true
    return fn.apply(this, args)
  }
}

/**
 * 将函数fn转为防抖函数。返回防抖函数。
 * @function debounce
 * @param {function} fn - 函数。
 * @param {number} [delay=0] - 可选,防抖动延迟时长,单位为ms,默认为0。
 * @returns {function}
 * @example
 * window.addEventListener('resize', U.debounce(() => {
 *   console.log(window.innerWidth);
 *   console.log(window.innerHeight);
 * }, 250));
 * // => 调整浏览器窗口尺寸,在250ms后控制台将打印一次窗口尺寸
 */
export const debounce = (fn, delay = 0) => {
  let timeoutId
  return function(...args) {
    clearTimeout(timeoutId)
    timeoutId = setTimeout(() => fn.apply(this, args), delay)
  }
}

/**
 * 将函数fn转为节流函数。返回节流函数。
 * @function throttle
 * @param {function} fn - 函数。
 * @param {number} wait - 节流时长,单位为ms。
 * @return {function}
 * @example
 * window.addEventListener('resize', U.throttle(function(evt) {
 *   console.log(window.innerWidth);
 *   console.log(window.innerHeight);
 * }, 250));
 * // 调整浏览器窗口尺寸,没间隔250ms控制台将打印一次窗口尺寸
 */
export 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))
    }
  }
}

/**
 * 管道函数,占位符“$”为上一个函数的运算结果,如:pipe(x, `a |> b($, y)`) 等价于 b(a(x), y)。
 * @function pipe
 * @param {*} param - 函数参数。
 * @param {string} line - 管道线。
 * @return {*}
 * @example
 * const x = 1;
 * const y = 3;
 * 
 * const a = n => n + 1;
 * const b = (x, y)=> x * y;
 * const c = n => n * n;
 * 
 * pipe(x, `a |> b($, y)`)
 * // => 6
 * 
 * pipe(x, `a |> c`)
 * // => 4
 */
export const pipe = (param, line) => {
  return line.split('|>')
    .reduce((acc, fn) => {
      fn = fn.indexOf('(') > -1
        ? fn.replace(/[\(|,]\s*\$\s*[\)|,]/g, w => w.replace('$', 'acc'))
        : `${fn}(acc)`
      return acc = new Function('acc', 'return ' + fn)(acc)
    }, param)
}