Skip to content

Higher-Order Function

高阶函数至少满足下列一个条件的函数:

  • 接受一个或多个函数作为参数
  • 返回一个函数作为结果
html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <label for="">价格: 2999.00</label>
    <button class="btn">打折</button>
    <script src="./1.js"></script>
    <script>
      const label = document.querySelector('label')
      const btn = document.querySelector('.btn')
      btn.onclick = () => {
        animation(1000, 2999, 299, (value) => {
          label.innerHTML = `价格: ${value.toFixed(2)}`
        })
      }
    </script>
  </body>
</html>
js
function animation(duration, from, to, callback) {
  const dist = to - from
  const speed = dist / duration
  const startTime = Date.now()
  let value = from
  console.log(value)
  callback(value)

  function _run() {
    const now = Date.now()
    const time = now - startTime
    // 动画结束
    if (time >= duration) {
      value = to
      console.log(value)
      callback(value)
      return
    }
    const d = time * speed
    value = from + d
    console.log(value)
    callback(value)
    requestAnimationFrame(_run)
  }

  requestAnimationFrame(_run)
}

对于这个通用函数animation,如果不传递第四个参数,就不能确定怎么使用它。但是传递了我就可以在回调函数里针对当前情况拿到animation计算好的值去使用

示例二

第二个参数是一个函数,用于生成分组的键值,也可以直接传入一个字符串名,表示根据该属性进行分组,做了参数归一化

js
const arr = [
  { name: 'zhangsan', age: 18, sex: 'male' },
  { name: 'lisi', age: 19, sex: 'female' },
  { name: 'wangwu', age: 23, sex: 'male' },
  { name: 'zhaoqi', age: 21, sex: 'male' },
  { name: 'liuqi', age: 22, sex: 'female' },
  { name: 'wangqi', age: 23, sex: 'male' }
]

function groupBy(arr, generateKey) {
  // 参数归一化
  if (typeof generateKey === 'string') {
    const propName = generateKey
    generateKey = (item) => item[propName]
  }
  const result = {}
  for (const item of arr) {
    const key = generateKey(item)
    if (!result[key]) {
      result[key] = []
    }
    result[key].push(item)
  }
  return result
}

console.log(groupBy(arr, 'sex'))
console.log(groupBy(arr, 'age'))

console.log(
  groupBy(arr, (item) => {
    return item.age
  })
)

console.log(
  groupBy(arr, (item) => {
    return item.sex
  })
)

// 可以按照 年龄和性别 分组
console.log(
  groupBy(arr, (item) => {
    return `${item.age} - ${item.sex}`
  })
)

示例三

把大量任务拆成小块执行,避免一次性循环导致页面卡死(长任务阻塞主线程)

html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <button id="clickBtn">插入10000条元素</button>
  </head>
  <body>
    <script src="./index.js"></script>
  </body>
</html>
js
const btn = document.getElementById('clickBtn')
const datas = new Array(100000).fill(0).map((_, i) => i)

btn.onclick = () => {
  const consumer = (item, i) => {
    const div = document.createElement('div')
    div.textContent = i
    document.body.appendChild(div)
  }
  //   const chunkSplitor = (task) => {
  //     setTimeout(() => {
  //       task((time) => time < 16.67)
  //     }, 30)
  //   }
  performChunk(100000, consumer)
}

function performChunk(datas, consumer, chunkSplitor) {
  if (typeof datas === 'number') {
    datas = new Array(datas)
  }
  if (datas.length === 0) {
    return
  }
  // 不传递chunkSplitor,默认使用requestIdleCallback
  if (!chunkSplitor && globalThis.requestIdleCallback) {
    chunkSplitor = (task) => {
      requestIdleCallback((idle) => {
        task(() => idle.timeRemaining() > 0)
      })
    }
  }

  let i = 0
  function _run() {
    if (i === datas.length) {
      return
    }
    chunkSplitor((hasTime) => {
      const now = Date.now()
      while (hasTime(Date.now() - now) && i < datas.length) {
        consumer(datas[i], i)
        i++
      }
      _run()
    })
  }
  _run()
}