防抖
利用定时器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)
}
}
深拷贝
手写 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
- 创建一个对象
- 将第一个参数作为构造函数
- 将新对象的原型指向构造函数的prototype
- 使用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
- 将第一个参数作为调用对象
- 给调用对象增加一个临时方法,也就是 this(调用者函数)
- 执行这个临时方法
- 删除临时方法
- 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
- 返回一个新函数
- 函数可以一直传参使用,所以需要 concat所有参数
- 作为构造函数使用需要注意 继承问题
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
}