手撕深拷贝deepCopy&浅拷贝shallowCopy
公共方法
// 判断数据类型
function types(v) {
return Object.prototype.toString.call(v).slice(8, -1).toLowerCase();
}
// 是否是基础数据类型
function isBasisData(v) {
return ["number", "string", "undefined", "null"].includes(types(v));
}
方法合集
function shallowCopy(v) {
let newObj = Array.isArray(v) ? [] : {};
if (isArr) v.forEach((e) => newObj.push(e));
if (!isArr) Object.keys(v).forEach((e) => (newObj[e] = v[e]));
return newObj;
}
/**
* @param {*} v 原数组
* @returns 拷贝完的新对象
* 使用示例:deepCopy([1, 2, { a: {b:[3,4]} },[5,6]])
*/
function deepCopy(v) {
let newObj = Array.isArray(v) ? [] : {};
for (let i = 0; i < v.length; i++) {
if (isBasisData(types(v[i]))) {
newObj[i] = v[i];
} else {
newObj[i] = deepCopy(v[i]);
}
}
return newObj;
}
// bubbleSort 冒泡(双重for循环)
function bubbleSort(val) {
if (!Array.isArray(val)) return [];
let v = shallowCopy(val);
for (let i = 0; i < v.length; i++) {
for (let j = 0; j < v.length; j++) {
if (v[j] > v[j + 1]) {
[v[j], v[j + 1]] = [v[j + 1], v[j]];
}
}
}
return v;
}
// quickSort 快排(基数化)
function quickSort(v) {
if (v.length < 2) return v;
let l = [],
c = [],
r = [],
point = v[Math.floor(v.length / 2)];
for (let i = 0; i < v.length; i++) {
if (v[i] < point) {
l.push(v[i]);
} else if (v[i] > point) {
r.push(v[i]);
} else if (v[i] === point) {
c.push(v[i]);
}
}
return quickSort(l).concat(...c, ...quickSort(r));
}
/** 防抖(延迟执行,多次触发重新计算)
* @param {*} fn 需要执行函数体
* @param {*} times 延迟时间
* @param {*} immediate 是否立即执行
* @returns 执行函数
* 使用示例: debounce(()=> {search()},props.throttleTime)
*/
function debounce(fn, times = 1000, immediate = false) {
let timer = null; // 延迟器
return function (...args) {
if (immediate && !timer) { // 立即执行
fn.apply(this, args);
}
if (timer) clearTimeout(timer); // 延时器存在就清空
timer = setTimeout(() => {
fn.apply(this, args);
}, times);
};
}
/** 节流(一定时间内多次触发只执行一次)
* @param {*} fn 需要执行函数体
* @param {*} throttleTime 节流间隔时间
* @returns 执行函数
* 使用示例: throttle(()=> {search()},props.throttleTime)
*/
function throttle(fn, throttleTime = 1000) {
let previous = 0; //老时间
return function (...args) {
let now = Date.now();
if (now - previous > throttleTime) {
previous = now; // 更新老时间
fn.apply(this, args);
} else {
console.log("没到时间");
}
};
}
// call, apply, bind
// 添加自定义的call
Function.prototype.myCall = function (currentThis, ...agrs) {
currentThis.myCall = this;
currentThis.myCall(...agrs);
delete currentThis.myCall;
};
// getA.myCall(obj,123,456)
// 添加自定义的apply
Function.prototype.myApply = function (currentThis, params) {
currentThis.myApply = this;
currentThis.myApply(params);
delete currentThis.myApply;
};
// getA.myApply(obj,[123,456])
// 添加自定义的bind
Function.prototype.myBind = function (currentThis, ...agrs) {
const fn = this;
return function () {
let thisArg = currentThis;
thisArg.myBind = fn;
fn.myCall(thisArg,...agrs);
delete thisArg.myBind;
};
};
// getA.myBind(obj, 1, 2,4)()
/**
* @param {*} custrogtor 构造函数
* @param {*} args 参数
* @returns obj 实例对象
* 使用示例:myNew(Person,'hahah',12)
*/
function Person(name, age) {
this.name = name;
this.age = age;
}
// new 关键字源码实现
function myNew(custrogtor, ...args) {
// 创建一个新对象,并指定原型
let obj = Object.create(custrogtor.prototype);
// 将函数 this 绑定到新对象
custrogtor.apply(obj,args);
// 返回新对象
return obj;
}
/** 倒计时升级版倒计时: 解决 setInterval如果前面有阻塞线程的任务,那么就可能会导致 setInterval 函数延迟问题
* requestAnimationFrame 允许以 60 帧/秒 (FPS) 的速率请求回调,而不会阻塞主线程。通过调用 requestAnimationFrame 方法浏览器会在下一次重绘之前执行指定的函数,这样可以确保回调在每一帧之间都能够得到适时的更新
* @param {*} 需要倒计时时长 (ms)
* @param {*}
* 使用示例 example4(60000);
*/
function example4(leftTime) {
let t = leftTime;
let timeout = null;
function start() {
requestAnimationFrame(() => {
t = t - 1000;
if (t < 0) {
timeout = null;
console.log("倒计时结束",timeout);
return;
}
timeout = setTimeout(() => {
console.log(t);
start();
}, 1000);
});
}
start();
}