手撕小汇总

防抖

javascript 复制代码
// 函数防抖的实现
function debounce(fn, wait) {
  let timer = null;

  return function() {
    let context = this,
        args = arguments;

    // 如果此时存在定时器的话,则取消之前的定时器重新记时
    if (timer) {
      clearTimeout(timer);
      timer = null;
    }

    // 设置定时器,使事件间隔指定事件后执行
    timer = setTimeout(() => {
      fn.apply(context, args);
    }, wait);
  };
}

节流

时间戳版(首段执行,无尾调用)

javascript 复制代码
// 函数节流的实现;
function throttle(fn, delay) {
  let curTime = Date.now();

  return function() {
    let context = this,
        args = arguments,
        nowTime = Date.now();

    // 如果两次时间间隔超过了指定时间,则执行函数。
    if (nowTime - curTime >= delay) {
      curTime = Date.now();
      fn.apply(context, args);
    }
  };
}

定时器版(首段延迟,有尾调用)

javascript 复制代码
function throttle(fn, delay) {
  let timer = null;
  return function () {
    const context = this;
    const args = arguments;
    if (!timer) {
      timer = setTimeout(() => {
        fn.apply(context, args);
        timer = null;
      }, delay);
    }
  };
}

融合版(首尾调用都有)

javascript 复制代码
function throttle(fn, delay) {
    // 上一次执行的时间戳
    let lastTime = Date.now();
    // 兜底定时器标识
    let timer = null;

    return function () {
        const context = this;
        const args = arguments;
        // 当前触发时间
        const now = Date.now();
        // 距离下一次允许执行还剩多久
        const remain = delay - (now - lastTime);

        // 场景1:剩余时间 <=0,达到间隔,可以立即执行
        if (remain <= 0) {
            // 如果存在等待中的兜底定时器,直接清除(防止重复执行)
            if (timer) {
                clearTimeout(timer);
                timer = null;
            }
            lastTime = now;
            fn.apply(context, args);
        } 
        // 场景2:时间还不足,且当前没有等待的定时器,则创建兜底任务
        else if (!timer) {
            timer = setTimeout(() => {
                lastTime = Date.now();
                timer = null;
                fn.apply(context, args);
            }, remain);
        }
    }
}

类型判断

javascript 复制代码
function getType(value) {
  // 单独处理两大特殊基础类型
  if (value === null) return 'null'
  if (value === undefined) return 'undefined'
  // 统一调用toString
  const rawType = Object.prototype.toString.call(value)
  // 截取 [object Xxx] 中间字符串
  return rawType.slice(8, -1).toLowerCase()
}

console.log(getType(null));              // 'null'
console.log(getType(undefined));         // 'undefined'
console.log(getType(123));               // 'number'
console.log(getType(123n));              // 'bigint'
console.log(getType('aaa'));             // 'string'
console.log(getType(true));              // 'boolean'
console.log(getType(Symbol()));          // 'symbol'
console.log(getType([]));                // 'array'
console.log(getType({}));                // 'object'
console.log(getType(function(){}));      // 'function'
console.log(getType(new Date()));        // 'date'
console.log(getType(/\w+/));             // 'regexp'
console.log(getType(new Map()));         // 'map'
console.log(getType(new Set()));         // 'set'

call

记得挂载函数,记得解构argument,记得解构args

javascript 复制代码
Function.prototype.myCall = function(context) {
  // 1. 校验调用者必须是函数
  if (typeof this !== 'function') {
    throw new TypeError('this is not a function');
  }
  // 2. 仅null/undefined替换为全局,其余值原样保留
  context = context ?? globalThis;
  // 3.原始值自动装箱
  context = Object(context);
  // 4. 创建唯一临时key,防止属性覆盖冲突
  const tempKey = Symbol('tempFunc');
  // 5. 挂载函数(此处this是调用call的函数)
  context[tempKey] = this;
  // 6. 截取参数,执行
  const args = [...arguments].slice(1);
  const result = context[tempKey](...args);
  // 7. 删除临时方法
  delete context[tempKey];
  return result;
};

apply

记得解构arguments,没传参记得加()

javascript 复制代码
function apply(ctx) {
    if (typeof this !== 'function') {
        throw new TypeError('type error')
    }
    let result = null
    ctx=ctx??globalThis
    ctx= Object(ctx)
    const tempKey=Symbol('tempFn')
    ctx[tempKey]=this
    if(arguments[1]){
        result = ctx[tempKey](...arguments[1])
    }else{
        result=ctx[tempKey]()
    }
    delete ctx[tempKey]
    return result
}

bind

解构argument,参数使用concat合并解构的argument

javascript 复制代码
// bind 函数实现
Function.prototype.myBind = function(context) {
  // 判断调用对象是否为函数
  if (typeof this !== "function") {
    throw new TypeError("Error");
  }
  // 获取参数
  let args = [...arguments].slice(1),
      fn = this;
  return function Fn() {
    // 根据调用方式,传入不同绑定值
    return fn.apply(
      this instanceof Fn ? this : context,
      args.concat(...arguments)
    );
  };
};

函数柯里化

javascript 复制代码
function curry(fn) {
  // fn.length 获取函数预期形参数量
  const len = fn.length
  return function curried(...args) {
    // 收集参数 >= 需要参数 → 执行
    if(args.length >= len) {
      return fn(...args)
    }
    // 参数不够,继续返回新函数收集
    return function(...newArgs) {
      return curried(...args, ...newArgs)
    }
  }
}

浅拷贝

javascript 复制代码
// 浅拷贝的实现;

function shallowCopy(object) {
  // 只拷贝对象
  if (!object || typeof object !== "object") return;

  // 根据 object 的类型判断是新建一个数组还是对象
  let newObject = Array.isArray(object) ? [] : {};

  // 遍历 object,并且判断是 object 的属性才拷贝
  for (let key in object) {
    if (object.hasOwnProperty(key)) {
      newObject[key] = object[key];
    }
  }

  return newObject;
}

深拷贝

javascript 复制代码
// 深拷贝的实现
function deepCopy(object) {
  if (!object || typeof object !== "object") return;

  let newObject = Array.isArray(object) ? [] : {};

  for (let key in object) {
    if (object.hasOwnProperty(key)) {
      newObject[key] =
        typeof object[key] === "object" ? deepCopy(object[key]) : object[key];
    }
  }

  return newObject;
}

数组求和

可多维求和

javascript 复制代码
const sum = arr.flat(Infinity).reduce((s, v) => s + v, 0)

数组扁平化

javascript 复制代码
let arr = [1, [2, [3, 4]]];
function flatten(arr) {
    while (arr.some(item => Array.isArray(item))) {
        arr = [].concat(...arr);
    }
    return arr;
}
console.log(flatten(arr)); //  [1, 2, 3, 4,5]

//或者直接用flat

用Promise实现图片的异步加载

javascript 复制代码
let imageAsync=(url)=>{
            return new Promise((resolve,reject)=>{
                let img = new Image();
                img.src = url;
                img.οnlοad=()=>{
                    console.log(`图片请求成功,此处进行通用操作`);
                    resolve(image);
                }
                img.οnerrοr=(err)=>{
                    console.log(`失败,此处进行失败的通用操作`);
                    reject(err);
                }
            })
        }
        
imageAsync("url").then(()=>{
    console.log("加载成功");
}).catch((error)=>{
    console.log("加载失败");
})

实现发布-订阅模式

javascript 复制代码
class EventBus {
  constructor() {
    // 存储 { 事件名: [回调函数列表] }
    this.events = Object.create(null);
  }

  // 订阅:on(事件名, 回调)
  on(eventName, callback) {
    if (!this.events[eventName]) {
      this.events[eventName] = [];
    }
    this.events[eventName].push(callback);
  }

  // 发布/触发:emit(事件名, 参数...)
  emit(eventName, ...args) {
    const cbs = this.events[eventName];
    if (!cbs) return;
    // 循环执行所有订阅回调,传入参数
    cbs.forEach(fn => fn(...args));
  }

  // 取消订阅:off(事件名, 指定回调)
  off(eventName, callback) {
    const cbs = this.events[eventName];
    if (!cbs) return;
    this.events[eventName] = cbs.filter(item => item !== callback);
  }

  // 一次性订阅:once,触发后自动解绑
  once(eventName, callback) {
    const wrapper = (...args) => {
      callback(...args);
      this.off(eventName, wrapper);
    };
    this.on(eventName, wrapper);
  }
}

once函数

函数只执行一次,后续调用直接返回第一次执行结果。

javascript 复制代码
function once(fn) {
  let result;
  let isRun = false;
//要异步的话就加个async,await
  return function (...args) {
    if (!isRun) {
      isRun = true;
      result = fn.apply(this, args);
    }
    return result;
  };
}

记忆函数

用对象 / Map 缓存函数入参对应的计算结果,重复传相同参数时直接返回缓存,不重复执行逻辑

javascript 复制代码
function memoize(fn) {
    const cache = new Map();

    // 序列化参数生成唯一字符串key
    function getKey(args) {
        return JSON.stringify(args);
    }

    return function (...args) {
        const key = getKey(args);
        if (cache.has(key)) {
            return cache.get(key);
        }
        const res = fn(...args);
        cache.set(key, res);
        return res;
    };
}
相关推荐
r_oo_ki_e_16 小时前
vue快速入门
前端·vue.js
虚惊一场16 小时前
把 CodeMirror 6 调教成 Markdown 编辑器:扩展、装饰与门面
前端·javascript·vue.js
lauo16 小时前
掌心核爆:iQOO首款小平板搭载2nm骁龙8E6,开启AI原生计算的移动终端新纪元
前端·人工智能·智能手机·重构·电脑·ai-native
唐青枫16 小时前
Java Kafka 实战指南:从 Topic、分区到 Spring Boot 可靠消息处理
java
虚惊一场16 小时前
一条 Markdown 渲染管线的全部细节:Worker、源行标注与按需加载
前端·javascript·vue.js
CoovallyAIHub16 小时前
当能源行业遇上 AI 智能体:Coco 为什么选择留在本地
前端·agent
程序员黑豆17 小时前
鸿蒙开发入门:以 Text 组件为例,掌握内置组件用法
前端·harmonyos
爱勇宝17 小时前
AI不会淘汰所有人,但会淘汰这6种人
前端·后端·程序员
脱胎换骨-军哥17 小时前
C++零成本抽象理论深度拆解:现代C++如何在不牺牲性能的前提下提供高级语法封装
java·开发语言·c++