JS基础与进阶(持续更新)

250 阅读2分钟

1.函数柯里化

参数复用

function CurryingCheck(reg) {
  return function(text) {
    return reg.test(text)
  }
}
const hasNumber = CurryingCheck(/\d+/g)
const hasLetter = CurryingCheck(/[a-z]+/g)
console.log(hasNumber('abc1')) // true
console.log(hasLetter('1234')) // false

循环调用

/*
 * 经典面试题,手写一个函数,使以下结果成立
 * add(1)(2)(3) => 6
 * add(1, 2, 3)(4) => 10
 * add(1, 2, 3)(4)(5) => 15
 */
function add() {
  let args = [...arguments]
  function fn() {
    args.push(...arguments)
    return fn
  }
  fn.toString = _ => args.reduce((prev, next) => prev + next)
  return fn
}

2.函数自执行的几种方式

注意:自执行函数前面的JS语句必须以分号结尾。

! function () {  // 函数前加特殊符号 ! + - ~ . void
  console.log(1)
}();
(function () {
  console.log(2)
})();
(function () {
  console.log(3)
}());

3.防抖

在指定时间连续调用防抖函数,防抖函数只执行一次。
1.立即执行版(即在指定时间内连续调用防抖函数,只执行第一次的定时函数)
2.非立即执行版(即在指定时间内连续调用防抖函数,只会执行最后一次的定时函数)

function debounce(fn, wait, immediate) {
  let T
  return function (...args) {
    if (T) {
      clearTimeout(T)
    } else if (immediate) {
      fn.apply(this, args)
    }
    T = setTimeout(() => {
      T = null
      if (!immediate) {
        fn.apply(this, args)
      }
    }, wait)
  }
}

4.节流

如果连续执行节流函数,只会在每个指定的时间内执行一次

1.时间戳版
缺点:如果倒数第一次的调用和倒数第二次的调用时间间隔小于指定时间,则最后一次的调用不会执行

function throttle(fn, wait) {
  let time = 0
  return function (...args) {
    const nowTime = Date.now()
    if (nowTime - time >= wait) {
      fn.apply(this, args)
      time = nowTime
    }
  }
}

2.定时器版
缺点:第一次触发,会在指定时间后才会执行

function throttle(fn, wait) {
  let T = null
  return function (...args) {
    if (!T) {
      T = setTimeout(() => {
        T = null
        fn.apply(this, args)
      }, wait)
    }
  }
}

3.时间戳与定时器结合版
优点:有时我们更希望第一次触发立即执行处理函数,最后一次触发也执行一次处理函数

function throttle(fn, wait) {
  let time = 0
  let T = null
  return function (...args) {
    const nowTime = Date.now()
    const remaining = nowTime - time - wait
    if (T) clearTimeout(T)
    if (remaining >= 0) {
      fn.apply(this, args)
      time = nowTime
    } else {
      T = setTimeout(() => {
        fn.apply(this, args)
      }, wait)
    }
  }
}

5.手写call、apply、bind 函数

1.call

Function.prototype.myCall = function (ctx, ...args) {
  let result
  if (ctx) {
    ctx._$ = this
    result = ctx._$(...args)
    delete ctx._$
  } else {
    result = this()
  }
  return result
}

2.apply

Function.prototype.myapply = function(ctx, args){
  let result
  if (ctx) {
    ctx._$ = this
    result = ctx._$(...args)
    delete ctx._$
  } else {
    result = this()
  }
}

3.bind

Function.prototype.mybind = function (ctx, ...args1) {
  let func = this
  return function (...args2) {
    if(!ctx){
      return func(...args2)
    }
    return func.call(ctx, ...args1, ...args2)
  }
}

平级数据转换为树形

function listToTree(list, key = "id", parentKey = "pid", rootValue = 0, childKey = "children") {
  const map = new Map();
  return list.reduce((res, item) => {
    if (item[parentKey] === rootValue) res.push(item);
    if (map.has(item[parentKey])) {
      map.get(item[parentKey])[childKey].push(item);
    } else {
      map.set(item[parentKey], { [childKey]: [item] });
    }
    map.set(item[key], Object.assign(item, map.get(item[key]) || { [childKey]: [] }));
    return res;
  }, []);
}