目录
一、深拷贝
二、防抖
三、节流
四、函数柯里化
五、instanceof
六、typeof
实现步骤:
function deepClone(target, map = new WeakMap()) {
// 基本类型直接返回
if (target === null || typeof target !== 'object') return target;
// 处理循环引用
if (map.has(target)) return map.get(target);
// 处理特殊对象类型
if (target instanceof Date) return new Date(target);
if (target instanceof RegExp) return new RegExp(target);
if (target instanceof Map) return new Map(Array.from(target, ([k, v]) => [deepClone(k, map), deepClone(v, map)]));
if (target instanceof Set) return new Set(Array.from(target, v => deepClone(v, map)));
// 创建新对象或数组(保持原型链)
const cloneTarget = Array.isArray(target)
? []
: Object.create(Object.getPrototypeOf(target));
// 记录拷贝关系(放在递归前,避免循环引用无限递归)
map.set(target, cloneTarget);
// 递归拷贝所有属性(包括Symbol)
Reflect.ownKeys(target).forEach(key => {
cloneTarget[key] = deepClone(target[key], map);
});
return cloneTarget;
}
// 测试循环引用和特殊对象
const original = {
date: new Date(),
regExp: /test/g,
map: new Map([['key', 'value']]),
set: new Set([1, 2, 3]),
nested: { a: 1 }
};
original.self = original;
const cloned = deepClone(original);
// 测试结果
console.log('深拷贝测试:');
console.log('日期对象:', cloned.date instanceof Date); // true
console.log('正则对象:', cloned.regExp instanceof RegExp); // true
console.log('Map对象:', cloned.map instanceof Map); // true
console.log('Set对象:', cloned.set instanceof Set); // true
console.log('循环引用:', cloned.self === cloned); // true
console.log('嵌套对象:', cloned.nested.a === 1); // true
防抖:在事件被频繁触发时,只有最后一次操作会被执行,中间的触发会被忽略。
当前实现模式:延迟执行,连续触发事件时,只在最后一次触发后的 delay
毫秒后执行一次
function debounce(fn, delay) {
let timer = null;
return function(...args) {
const context = this;
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(context, args);
}, delay);
};
}
如果没有显式保存上下文:
this
let box = document.querySelector('.box')
let x = 1
const func = () => {
box.textContent = x;
console.log(x);
x++;
}
const debounced = debounce(func, 1000)
box.addEventListener('mousemove', debounced)
节流:在事件被频繁触发时,固定时间间隔内只执行一次操作,忽略中间的触发。
// 立即执行--时间戳
function throttle(fn, delay) {
let lastTime = 0;
return function (...args) {
const context = this;
const now = Date.now();
if (now - lastTime >= delay) {
fn.apply(context, args);
lastTime = now;
}
};
}
// 延迟执行--定时器
function throttle(func, delay) {
let timeout = null;
return function (...args) {
const context = this;
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args);
}, delay);
}
};
}
let box = document.querySelector('.box')
let x = 1
const func = () => {
box.textContent = x;
console.log(x);
x++;
}
const throttled = throttle(func, 1000);
box.addEventListener('mousemove', throttled);
总结:
模式 | 特性描述 | 应用场景 |
---|---|---|
立即执行防抖 | 首次立即执行,之后间隔期内不执行,直到停止触发超过wait 时间后,才能再次立即执行。 |
登录按钮防止重复提交 |
延迟执行防抖 | 只有最后一次触发后等待wait 时间才会执行 |
搜索框联想建议 |
立即执行节流 | 首次立即执行,之后间隔执行 | 页面滚动加载 |
延迟执行节流 | 每次触发后都在延迟结束时执行 | 每次操作都需要响应的场景 |
首尾执行节流 | 首次触发立即执行,后续触发保证延迟结束再执行 | 鼠标移动跟随效果 |
防抖函数扩展方向:
添加立即执行选项
immediate
参数控制是否立即执行immediate=true
时,首次触发立即执行并设置冷却期,冷却期内新触发重置计时器支持取消执行
cancel()
方法清除定时器最大等待时间
maxWait
参数确保不会无限延迟节流函数扩展方向:
添加延迟执行模式
时间戳+定时器结合
节流状态查询
isPending()
方法查询是否处于冷却期柯里化函数:分步传递参数,每次接收一个参数并返回新函数。
function curry(fn) {
return function curried(...args) {
// 参数数量足够时直接执行
if (args.length >= fn.length) {
return fn.apply(this, args);
} else {
// 参数不足时返回新函数
return function(...nextArgs) {
return curried.apply(this, [...args, ...nextArgs]);
}
}
};
}
const add = (a, b, c) => a + b + c;
const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)); // 6
console.log(curriedAdd(1, 2)(3)); // 6
instanceof:用于检查一个对象是否是某个构造函数的实例。
实现步骤:
right
是否为构造函数(函数类型)left
为原始值(null
/非对象)直接返回 false
left
的当前原型 proto
prototype
属性true
proto === null
)返回 false
function myInstanceof(obj, fn) {
// 校验右侧为构造函数
if (typeof fn !== 'function') {
throw new Error();
}
// 过滤原始值
if (obj === null || typeof obj !== 'object') {
return false;
}
let proto = Object.getPrototypeOf(obj);
const prototype = fn.prototype;
while (proto !== null) {
if (proto === prototype) return true;
proto = Object.getPrototypeOf(proto);
}
return false;
}
class Parent {}
class Child extends Parent {}
const obj = new Child();
console.log('\ninstanceof 测试:');
console.log('myInstanceof(obj, Child):', myInstanceof(obj, Child)); // true
console.log('myInstanceof(obj, Parent):', myInstanceof(obj, Parent)); // true
console.log('myInstanceof(obj, Object):', myInstanceof(obj, Object)); // true
console.log('myInstanceof([], Array):', myInstanceof([], Array)); // true
console.log('myInstanceof(123, Number):', myInstanceof(123, Number)); // false
typeof:用于返回一个值的数据类型字符串。
null
返回 'null'
typeof
结果Object.prototype.toString.call(value)
[object Type]
中的 Type
部分'Array'
→ 'array'
)function myTypeof(value) {
// 处理 null 的特殊情况
if (value === null) return 'null';
// 处理引用类型
if (typeof value === 'object') {
// 使用 Object.prototype.toString 精确判断类型
const typeString = Object.prototype.toString.call(value);
// 提取类型字符串中的实际类型
return typeString.slice(8, -1).toLowerCase();
}
// 处理基本类型
return typeof value;
}
// 测试用例
console.log(myTypeof(42)); // 'number'
console.log(myTypeof('hello')); // 'string'
console.log(myTypeof(true)); // 'boolean'
console.log(myTypeof(undefined)); // 'undefined'
console.log(myTypeof(null)); // 'null'
console.log(myTypeof({})); // 'object'
console.log(myTypeof([])); // 'array'
console.log(myTypeof(/regex/)); // 'regexp'
console.log(myTypeof(new Date())); // 'date'
console.log(myTypeof(() => {})); // 'function'
console.log(myTypeof(Symbol())); // 'symbol'
console.log(myTypeof(new Map())); // 'map'
console.log(myTypeof(new Set())); // 'set'