JS手写练习(一)

139 阅读3分钟

1. 实现curry()

请实现一个curry()方法,接受一个function然后返回一个柯里化过后的function

栗子:

const join = (a, b, c) => {
   return `${a}_${b}_${c}`
}
const curriedJoin = curry(join)
curriedJoin(123// '1_2_3'
curriedJoin(1)(23// '1_2_3'
curriedJoin(12)(3// '1_2_3'

实现:

  • 递归实现
  • 每次调用收集参数,直到参数个数等于传入函数的参数个数就执行传入函数

代码:

/**
 * @param { (...args: any[]) => any fn
 * @returns { (...args: any[]) => any }
 */
function curry(fn) {
  // your code here
  return function myCurry(...args) {
    if (args.length>=fn.length) {
      return fn(...args)
    } else {
      return (...args2) => {
        return myCurry(...args,...args2)
      };
    }
  }
}

2. 实现支持placeholder的curry()

请实现一个支持placeholder的curry(),可以像这样使用。placeholder为占位符形式。

栗子:

const  join = (a, b, c) => {
   return `${a}_${b}_${c}`
}
const curriedJoin = curry(join)
const _ = curry.placeholder
curriedJoin(123// '1_2_3'
curriedJoin(_, 2)(13// '1_2_3'
curriedJoin(_, _, _)(1)(_, 3)(2// '1_2_3'

实现:

  • 递归实现
  • 判断complate,所有的参数不包含placeholder,并且参数长度和传入函数参数长度一样
  • 后面的参数要替换前面的placeholder

代码:

/**
 * @param { (...args: any[]) => any fn
 * @returns { (...args: any[]) => any }
 */
function curry(fn) {
    // your code here
    return function myCurry(...args) {
        let complate = args.length >= fn.length && args.slice(0, fn.length - 1).every(ele => ele !== curry.placeholder);
        if (complate) {
            return fn(...args)
        } else {
            return (...args2) => {
                const args22 = args2;
                let curryArgs = args.map(ele => {
                    if (ele === curry.placeholder && args22.length > 0) {
                        return args22.shift()
                    } else {
                        return ele
                    }
                });
                if (args22.length > 0) {
                    curryArgs.push(...args22)
                }
                return myCurry(...curryArgs)
            };
        }
    }
}
curry.placeholder = Symbol();

3. 实现Array.prototype.flat()

Array.prototype.flat()可以用来扁平化数组。你能够自己实现一个flat么?

栗子:

const arr = [1, [2][3, [4]]];
flat(arr)
// [1, 2, 3, [4]]
flat(arr, 1)
// [1, 2, 3, [4]]
flat(arr, 2)
// [1, 2, 3, 4]

实现:

  • 递归实现
  • 判断complate,depth为0或者所有元素都不是数组类型
  • 每层处理后depth-1

代码:

/**
 * @param { Array arr
 * @param { number depth
 * @returns { Array }
 */
function flat(arr, depth = 1) {
  // your imeplementation here
  const complate = depth === 0 || arr.every(item => !(item instanceof Array));
  if (complate) return arr;
  let res = [];
  arr.forEach(item => {
      if (item instanceof Array) {
          res.push(...item)
      } else {
          res.push(item)
      }
  })
  return flat(res, depth - 1)
}

本文使用 markdown.com.cn 排版

4. 手写throttle()

请实现一个throttle()方法,throttle(func, delay)返回一个function,这个function无论多么频繁地调用,原始的func的调用也不会超过指定的频率。

栗子:

AB─C─ ─D─ ─ ─ ─ ─ ─ E─ ─F─G

按照3个单位进行throttle过后

─A─ ─ ─C─ ─ ─D ─ ─ ─ ─ E─ ─ ─G

A因为不在任何的冷却时间,所以立即被执行
B被跳过了,因为B和C都在A的冷却时间里。

实现:

  • 返回一个函数return ()=>{}
  • 闭包记录最后一次参数lastArgs
  • 在冷却时间内就保存参数lastArgs,否则返回执行函数

代码:

/**
 * @param {(...args:any[]) => anyfunc
 * @param {numberwait
 * @returns {(...args:any[]) => any}
 */
function throttle(func, wait) {
  let waitting = false; 
  let lastArgs = null;
  return (...args)=>{
    //如果不在冷却中
    if(!waitting){
      waitting = true;
      setTimeout(()=>{
        //经过wait时间再刷新冷却waitting
        waitting = false;
        //如果lastArgs不为空就返回保存的参数的执行函数func(...lastArgs)
        if(lastArgs) return func(...lastArgs);
      },wait)
      return func(...args);
    }else{
      //如果在冷却中就保存lastArgs
      lastArgs = args
    }
  }
}

5. 手写throttle()并支持leading 和 trailing

实现一个增强的throttle(),使其支持第三个参数option: {leading: boolean, trailing: boolean}

  1. leading: 是否立即执行
  2. trailing: 是否在冷却后执行

栗子:

同样地按照之前的3单位的throttle来举例。

─A─B─C─ ─D─ ─ ─ ─ ─ ─ E─ ─F─G

用{leadingtruetrailingtrue}来throttle后,我们得到

─A─ ─ ─C─ ─ ─D ─ ─ ─ ─ E─ ─ ─G

如果是 {leadingfalsetrailingtrue},A 和 E 被跳过了

─ ─ ─ ─C─ ─ ─D─ ─ ─ ─ ─ ─ ─G

如果是 {leadingtruetrailingfalse},只有 A D E 被保留

─A─ ─ ─ ─D─ ─ ─ ─ ─ ─ E

如果是 {leadingfalsetrailingfalse},显而易见,什么都不会发生

实现:

  • 返回一个函数return ()=>{}
  • 闭包记录最后一次参数lastArgs
  • 如果有timer就表示冷却中,如果没有立即执行就保存参数

代码:

/**
 * @param {(...args: any[]) => any} func
 * @param {number} wait
 * @param {boolean} option.leading
 * @param {boolean} option.trailing
 * @returns {(...args: any[]) => any}
 */
function throttle(func, wait, option = {leading: true, trailing: true}) {
  const {leading, trailing} = option;
  let lastArgs = null;
  let that = null;   
  let timer = null;
  //返回的函数
  return (...args)=>{
    that = this;
    //如果存在定时器,就是在冷却中
    if(timer){
      lastArgs = args;
      return
    }
    //如果是立即执行就执行,否则保存参数
    if(leading){
      func.call(that,...args)
    }else{
      lastArgs = args;
    }
    //设置冷却定时器
    timer = setTimeout(()=>{
      //如果trailing,否则完成冷却
      if(trailing && lastArgs){
        func.call(that,...lastArgs)
        lastArgs = null;
        that = null;
        //trailing之后要再设置冷却,并且在冷却之后要继续trailing
        setTimeout(()=>{
          timer = null;
          if(trailing && lastArgs){
            func.call(that,...lastArgs)
            lastArgs = null;
            that = null;
          }
        },wait)
      }else{
        timer = null
      }
    },wait)
  }
}

未完待续。。。