👊 多看,多听,多读,多应用。
防抖 / 节流
防抖:
在事件被触发n
秒后,再执行回调。
function debounce(fn, wait) {
// 声明一个timer变量用于存储定时器ID,初始化为null
var timer = null;
// 返回一个新的函数(闭包)
return function() {
// 保存当前执行上下文(this)和参数(arguments)
var context = this,
args = arguments;
// 检查是否已有定时器存在
if (timer) {
// 如果存在定时器,清除它(取消之前的等待执行)
clearTimeout(timer);
// 将timer重置为null
timer = null;
}
// 设置一个新的定时器
timer = setTimeout(() => {
// 定时器回调:使用apply调用原始函数,保持正确的this和参数
fn.apply(context, args);
}, wait); // 等待wait毫秒后执行
};
}
节流:
function throttle(fn, delay) {
// 记录上一次执行的时间戳,初始化为当前时间
var preTime = Date.now();
// 返回一个新的函数(闭包)
return function() {
// 保存当前执行上下文(this)和参数(arguments)
var context = this,
args = arguments,
// 获取当前时间戳
nowTime = Date.now();
// 检查当前时间与上次执行时间的间隔是否超过设定的延迟时间
if (nowTime - preTime >= delay) {
// 更新上次执行时间为当前时间
preTime = Date.now();
// 调用原始函数,并返回其结果
return fn.apply(context, args);
}
};
}
深 / 浅 拷贝
浅
1、object.assign 浅
Object.assign(target, ...sources)
比如:
let target = {};
let source = { a: { b: 1 } };
Object.assign(target, source);
console.log(target); // { a: { b : 1 } }
object.assign
有缺点:
有两个不能拷贝:
1、对象继承属性、2、不可枚举属性
怎么讲,我们来看一个例子:
let obj1 = {
a: {
b: 1
},
sym: Symbol(1)
}
Object.defineProperty(obj1, 'innumerable', {
value: '不可枚举',
enumberable: false,
});
let obj2 = {};
Object.assign(obj2, obj1);
obj1.a.b = 2;
console.log('obj1', obj1);
console.log('obj2', obj2);
由上面例子可以知道,Symbol类型是可以通过Object.assign()
去拷贝得到的。
2、扩展运算符 浅
let cloneObj = { ...obj };
跟object.assign
一样,只能浅拷贝
3、concat拷贝数组 浅
let a = [1, 2, 3];
let b = a.concat();
b[1] = 4;
console.log(a); // [1, 2, 3]
console.log(b); // [1, 4, 3]
slice拷贝数组 浅
// slice要这样用:arr.slice(start, end)
let a = [1, 2, { val: 3 }];
let b = a.slice();
b[2].val = 100;
console.log(a); // [1, 2, { val: 100 }]
浅,只能拷一层对象,如果多层,浅拷贝就GG,而且它a会和被克隆成它的那个b,其实两者指向的是同一个地址,a变,b改,b改,a也会变。
那好,实现一下浅拷贝吧:
// 思路:两点。
// 一个点:基础类型 直给
// 另一个点:引用类型 就一层完事
const shallowClone = (target) => {
if (typeof target === 'object' && target !== null) {
const cloneTarget = Array.isArray(target) ? []: {};
for (let prop in target) {
if (target.hasOwnProperty(prop)) {
cloneTarget[prop] = target[prop];
}
}
return cloneTarget;
} else {
return target;
}
}
深
浅指向同一块地址,那么深就是开一块新的内存地址。
JSON.stringify
let a = {
name: 'kk',
age: 18,
jobs: {
first: 'waiter',
second: 'manager'
}
}
let b = JSON.parse(JSON.stringify(a)) // JSON.parse 将 JSON 字符串生成一个新的对象
a.jobs.first = 'cleaner'
console.log(b.jobs.first) // waiter
JSON.stringify
完美吗?
不。有缺点:
1、undefined、symbol 拷贝不过来,会被忽略掉;对象有NaN
、Infinity
、-Infinity
、JSON
序列化会变成null
2、不能序列化函数
;
3、无法拷贝不可枚举
的属性和对象的原型链
;
4、拷贝RegExp
引用类型会变成空对象
;
5、拷贝Date
会变成字符串
;
6、不能解决循环引用的对象,就是 对象成环 obj[key] = obj
。
手写
const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null)
const deepClone = function (obj, hash = new WeakMap()) {
// Date
if (obj.constructor === Date) {
// 日期对象直接返回一个新的日期对象
return new Date(obj)
}
// 正则
if (obj.constructor === RegExp){
//正则对象直接返回一个新的正则对象
return new RegExp(obj)
}
//如果循环引用了就用 weakMap 来解决
if (hash.has(obj)) {
return hash.get(obj)
}
let allDesc = Object.getOwnPropertyDescriptors(obj)
//遍历传入参数所有键的特性
let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc)
//继承原型链
hash.set(obj, cloneObj)
for (let key of Reflect.ownKeys(obj)) {
cloneObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key], hash) : obj[key]
}
return cloneObj
}
lodash库
下工具库去用。
const _ = require('lodash');
const originalObj = {
a: 1,
b: {
c: 2,
d: [3, 4, 5]
}
};
// 使用 cloneDeep 进行深拷贝
const clonedObj = _.cloneDeep(originalObj);
// 修改克隆后的对象不会影响原对象
clonedObj.b.c = 20;
console.log(originalObj.b.c); // 仍然是 2
充满热情,精力旺盛,I love my job! I love my job! I love my job! 👅
多阅读,多尝试。结合指导,运用于实际。