防抖
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;
};
}