手写防抖,节流,深拷贝,柯里化,new,call,apply,bind

820 阅读2分钟

防抖

利用定时器setTimeout

function debounce(fn,ms){
  let timer
  return function(...args){
    if(timer){
      clearTimeout(timer)
    }
    timer=setTimeout(()=>{
      fn.apply(this,args)
    },ms)
  }
}

节流

相对于防抖,意义在于,在限制的时间内必然执行一次函数

function throttle(fn,ms){
  let canRun=true
  return function(...args){
    if(!canRun) return
    canRun=false
    setTimeout(()=>{
    fn.apply(this,args)
      canRun=true
    },ms)
  }
}

深拷贝

javascript深浅拷贝详解

手写 deepClone

function deepClone(obj, cache = new WeakMap()) {
  if (!obj instanceof Object) return obj
  // 防止循环引用
  if (cache.get(obj)) return cache.get(obj)
  // 支持函数
  if (obj instanceof Function) {
    return function () {
      return obj.apply(this, arguments)
    }
  }
  // 支持日期
  if (obj instanceof Date) return new Date(obj)
  // 支持正则对象
  if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags)

  // 数组是 key 为数字索引的特殊对象
  const res = Array.isArray(obj) ? [] : {}
  // 缓存 copy 的对象,用于处理循环引用的情况
  cache.set(obj, res)

  Object.keys(obj).forEach((key) => {
    if (obj[key] instanceof Object) {
      res[key] = deepClone(obj[key], cache)
    } else {
      res[key] = obj[key]
    }
  });
  return res
}

柯里化

function curry(func) {
  return function curried(...args) {
  //如果传入的参数多余函数定义的参数,返回结果
  //func.length 返回fn声明的参数个数
    if (args.length >= func.length) {
      return func.apply(this, args)
    }
    //否则返回一个待执行函数
    return function (...args2) {
      return curried.apply(this, [].concat(args).concat(args2))
    }
  }
}

// 测试
function sum (a, b, c) {
  return a + b + c
}
const curriedSum = curry(sum)
console.log(curriedSum(1, 2, 3))
console.log(curriedSum(1)(2,3))
console.log(curriedSum(1)(2)(3))

原生new特点

function Person(name,age){
  this.name=name;
  this.age=age;
  //如果return null或undefined或基本类型,则new实例不影响,否则返回构造函数的返回值
}

手写new

  1. 创建一个对象
  2. 将第一个参数作为构造函数
  3. 将新对象的原型指向构造函数的prototype
  4. 使用apply改变this指向并执行构造函数
function myNew(Func,...args){
  let instance={}
  if(Func.prototype){
  	Object.setPrototypeOf(instance,Func.prototype)//instance.__proto__=Func.prototype
  }
  const res=Func.apply(instance,args)
  return typeof res==='function'||(typeof res==='object'&& res!==null)?res:instance;
}

手写call

  1. 将第一个参数作为调用对象
  2. 给调用对象增加一个临时方法,也就是 this(调用者函数)
  3. 执行这个临时方法
  4. 删除临时方法
  5. return 结果

Function.prototype.myCall=function(context,...args){
  if(context==null||context==undefined){
    context=window
  }else{
    context=Object(context)
  }
  const symbolCall=Symbol('call')
  context[symbolCall]=this
  const res=context[symbolCall](args)
  delete context[symbolCall]
  return res
}

手写apply

与call唯一的区别是参数的形式

Function.prototype.myApply=function(context,args){
  if(context==null||context==undefined){
      context=window
  }else{
      context=Object(context)
  }
  const symbolApply=Symbol("apply")
  context[symbolApply]=this
  const res=context[symbolApply](...args)
  delete context[symbolApply]
  return res
}

手写bind

  1. 返回一个新函数
  2. 函数可以一直传参使用,所以需要 concat所有参数
  3. 作为构造函数使用需要注意 继承问题

Function.prototype.myBind=function(context,...args){
  let fn=this
  const newFunc=function(){
  	const newArgs=[].concat(args).concat(...arguments)
    //如果是new调用
    if(this instanceof newFunc){
    	fn.apply(this,newArgs)
    }else{//正常调用
    	fn.apply(context,newArgs)
    }
  }
  //解决继承问题,防止修改了newFunc函数的prototype而影响原函数的原型链
  newFunc.prototype=Object(fn.prototype)
  return newFunc
}

其他类似写法

github.com/haizlin/fe-…