手撕前端常写代码

130 阅读3分钟

防抖,节流。

  • 防抖经常出现在用户提交表单上。当网速慢的时候,用户得不到第一时间的反馈就可能在次点击。造成二次发送请求。可能直接导致失败。这个时候可以在点击事件上外加一层防抖函数。具体思路就是当一段时间内如果用户再次点击的话将之前的事件取消。来消除事件多次执行而失败的目的。
function debounce(fn, wait) {
  let timer  //写一个变量来保存一个定时器
  return (...args) => {
    if(timer) clearTimeout(timer) //判断在wait事件内timer是否存在,若存在清除
    timer = setTimeout(() => { //重新设置
      fn.apply(this, args)
    }, wait)
  }
}
  • 节流就是在一段事件内事件只可以触发一次。例如轮播图的滚动问题,多次点击只有第一次点击是有效的
function throttle(fn, wait) {
  let timer  //写一个变量来保存一个定时器
  return (...args) => {
    if(timer) return //判断在wait事件内timer是否存在,若存在直接返回
    timer = setTimeout(() => { 
      fn.apply(this, args)
    }, wait)
  }
}

经典的promise

  • promise是面试的必问题,手写一个promise可以更好的理解。一个promise主要就是状态,不同状态的回调函数放在一个栈中维护
class newProm { //promise通过new创造当然使用一个构造函数
  static resolve(val) {  //static是可以直接调用
    if(val && val.then) {  //若有值,并且是一个实例(上面有方法then)返回值
      return val
    }
    return new newProm((resolve) => resolve(val)) //为空时(说明直接调用的)返回一个newProm实例
  }
  constructor(fn) {
    this.status = 'PENDING' //状态
    this.value = undefined //保存值
    this.reason = undefined 
    
    this.resolveFns = []  //成功时的回调
    this.rejectFns = []  //失败的时的回调


    const resolve = (value) => { //resolve函数
      setTimeout(() => { //定时器模拟异步
        this.status = 'RESOLVED' //改变状态
        this.value = value  //设置值
        this.resolveFns.forEach(({ fn, resolve: res, reject: rej }) => res(fn(value))) //执行所有(使用了结构赋值)
      })
    }

    const reject = (e) => {
      setTimeout(() => {
        this.status = 'REJECTED'
        this.reason = e
        this.rejectFns.forEach(({ fn, resolve: res, reject: rej }) => rej(fn(e)))
      })
    }

    fn(resolve, reject)
  }
  then(fn) { //then函数
    if (this.status === 'RESOLVED') {
      const result = fn(this.value)
      // 需要返回一个 newProm
      // 如果状态为 resolved,直接执行
      return newProm.resolve(result)
    }
    if (this.status === 'PENDING') {
      // 也是返回一个 newProm
      return new newProm((resolve, reject) => {
        // 推进栈中,之后统一执行 ,箭头函数,this直接绑定。这个this不是新的newProm
        this.resolveFns.push({ fn, resolve, reject }) 
      })
    }
  }

  catch (fn) {
    if (this.status === 'REJECTED') {
      const result = fn(this.value)
      return newProm.resolve(result)
    }
    if (this.status === 'PENDING') {
      return new newProm((resolve, reject) => {
        this.rejectFns.push({ fn, resolve, reject })  
      })
    }
  }
}

call, apply , bind

  • call
Function.prototype.newCall = function (context) {
    let context = context || window; //判断是否传值,若无指向window
    context.fn = this; //在传入的context上保存函数,在context上的fn内部的this必定指向自身啦
    let args = []; //维护一个数组
    let len = arguments.length
    for(var i = 1; i < len; i++) {
        args.push('arguments[' + i + ']');  //将参数在数组中保存 
    }
    let result = eval('context.fn(' + args +')'); //执行 这里args相当于直接执行join()
    delete context.fn //删除
    return result; 返回结果
}
  • apply :apply和call只是传参的不同
Function.prototype.newApply = function (context, arr) {
        let context = context || window;
        context.fn = this
        let result;
        let args = [];
        let len = arr.length
        for (var i = 0; i < len; i++) {
            args.push('arr[' + i + ']');
        }
        result = eval('context.fn(' + args + ')')
        delete context.fn
    return result;
}
  • bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。
Function.prototype.newBind = function (context) {

    if (typeof this !== "function") {
      throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
    }  //调用的不是函数就报错
    var self = this; //保存this
    var args = Array.prototype.slice.call(arguments, 1); //保存参数
    var fNOP = function () {}; 

    var fbound = function () {  //bind返回的是一个函数
        // 当作为普通函数时,this 指向 window,self 指向绑定函数,此时结果为 false,当结果为 false 的时候,this 指向绑定的 context。
        self.apply(this instanceof self ? this : context, args.concat(Array.prototype.slice.call(arguments))); //参数合并
    }

    fNOP.prototype = this.prototype; 
    fbound.prototype = new fNOP(); //继承函数原型上的值

    return fbound;

}
  • 这三个实现学习了伢羽大大的文章(非常nice,大家可以去看看,他的js深入系列写的很好)juejin.cn/user/712139…