实现深拷贝
方法一
javascript
function deepCopy(oldobj, newobj){
for(let item in oldobj){
let value = oldobj[item]
if(Array.isArray(value)) {
newobj[item] = [];
deepCopy(value, newobj[item]);
}
else if(value instanceof Object) {
newobj[item] = {};
deepCopy(value, newobj[item]);
}
else{
newobj[item] = value;
}
}
}
方法二 带有循环依赖检测的深拷贝
参考自Vuex源码
typescript
const deepClone = (obj: Object | null, caches:{ original:any, copy: any }[] = []): Object | null => {
if(obj === null || typeof obj !== 'object') {
return obj;
}
const hitCache = caches.find((cache) => cache.original === obj);
if(hitCache) {
return hitCache.copy;
}
const copy = Array.isArray(obj) ? [] : {};
caches.push({
original: obj,
copy
});
Object.keys(obj).forEach((key) => {
copy[key] = deepClone(obj[key], caches);
});
return copy;
}
方法3 使用map记录
javascript
const deepClone = (sourceObj, cache = new Map()) => {
if(typeof sourceObj !== 'object' || sourceObj === null) return sourceObj;
if(cache.has(sourceObj)) return cache.get(sourceObj);
// 深拷贝初始值:对象/数组
let copy = Array.isArray(sourceObj) ? [] : {};
// 防止循环引用
cache.set(sourceObj, copy);
Reflect.ownKeys(sourceObj).forEach(key => {
if (sourceObj[key] instanceof Date) {
// 判断日期类型
copy[key] = new Date(sourceObj[key].getTime());
} else if (sourceObj[key] instanceof RegExp) {
// 判断正则类型
copy[key] = new RegExp(sourceObj[key]);
} else {
// 当元素属于对象(排除 Date、RegExp、DOM)类型时递归拷贝
copy[key] = (typeof sourceObj[key] === 'object') ? deepClone(sourceObj[key], cache) : sourceObj[key];
}
})
return copy;
}
实现 new
typescript
function newObj(_class, ...rest) {
const obj = Object.create(_class.prototype);
const returnObj = _class.call(obj, ...rest);
return typeof returnObj === 'object' ? returnObj : obj;
}
实现 Object.create
javascript
function myCreate(proto){
function F () {}
F.prototype = proto;
return new F();
}
实现sum(1)(2)(3)
javascript
function currySum(fn) {
const param = [];
const _ = function (...rest) {
if(rest.length) {
param.push(...rest);
return _;
} else {
return fn.apply(null, param);
}
}
return _;
}
const _sum = (...rest) => {
return rest.reduce((prev, item) => {
return prev + item;
}, 0);
}
const sum = currySum(_sum);
const result = sum(1)(2)(3, 4)();
console.log(result); // 10
实现ES6 extends
javascript
function Father(foo) {
this.content = [foo];
}
Father.prototype.someFunc = function (bar) {
this.content.push(bar);
console.log(this.content);
};
function Son(foo) {
Father.call(this, foo);
}
function extend() {
const sonPrototype = Object.create(Father.prototype);
// 修正原型中的constructor指向
sonPrototype.constructor = Son;
Son.prototype = sonPrototype;
}
extend();
const son = new Son(2);
const father = new Father(1);
console.log(Son.prototype);
son.someFunc(3); // [2, 3];
father.someFunc(4); // [1, 4]; // 不受影响
console.log(son instanceof Son, son instanceof Father); // true true 里氏替换原则 子类是父类的实例
console.log(father instanceof Son, father instanceof Father); // false true
Son.prototype.anotherFunc = () => {}; // 不影响父类
console.log(Son.prototype.__proto__ === Father.prototype) // true
防抖
javascript
const debounce = (fn, wait) => {
let timer;
return function (...args) {
if(timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.call(this,...args);
}, wait);
}
}
节流
javascript
const throttle = (fn, wait) => {
let prev = Date.now();
return function (...args) {
const now = Date.now();
if(now - prev > wait) {
fn.call(this, ...args);
prev = now;
}
}
}
深比较 1
javascript
function isEqual(sourceObj, compareObj) {
let sourceType = getType(sourceObj);
// 1.比较类型
if (sourceType !== getType(compareObj))
return false;
//2.数组对比
else if (sourceType === "Array") {
if (sourceObj.length !== compareObj.length) return false;
if (sourceObj.length === 0) return true;
for (let i = 0; i < sourceObj.length; i++) {
if (!isEqual(sourceObj[i], compareObj[i])) //递归比较每一个
return false;
}
return true;
}
// 3.对象比较
else if (sourceType === "Object") {
// Object.keys() 只包含可枚举属性 Object.getOwnPropertyNames 包含所有属性, 但不包含 Symbol
// 两者都不包含原型链上的属性 相当于自带 Object.hasOwnProperty();
// Reflect.ownKeys === Object.getOwnPropertyNames().concat(Object.getOwnPropertySymbols());
let sourceKeyList = Reflect.ownKeys(sourceObj);//包括Symbol
let compareKeyList = Reflect.ownKeys(compareObj);
if (sourceKeyList.length !== compareKeyList.length) //数量不一致
return false;
for (let i = 0; i < sourceKeyList.length; i++) {
let key = sourceKeyList[i];
if (key !== compareKeyList[i]) //比较键名
return false;
if (!isEqual(sourceObj[key], compareObj[key])) //递归比较
return false;
}
return true;
}
//4.Set Map 比较
else if (sourceType === "Set" || sourceType === "Map") {
// 把 Set Map 转为 Array
return isEqual(Array.from(sourceObj), Array.from(compareObj));
}
//5.其余情况直接===比较
return sourceObj === compareObj;
}
深比较2
javascript
function isObjectValueEqual(a, b) {
// 判断两个对象是否指向同一内存,指向同一内存返回true
if (a === b) return true;
// 获取两个对象键值数组
let aProps = Object.getOwnPropertyNames(a);
let bProps = Object.getOwnPropertyNames(b);
// 判断两个对象键值数组长度是否一致,不一致返回false
if (aProps.length !== bProps.length)
return false;
// 遍历对象的键值
for (let prop in a) {
// 判断a的键值,在b中是否存在,不存在,返回false
if (b.hasOwnProperty(prop)) {
// 判断a的键值是否为对象,是则递归,不是对象直接判断键值是否相等,不相等返回false
if (typeof a[prop] === 'object') {
if (!isObjectValueEqual(a[prop], b[prop])) return false;
} else if (a[prop] !== b[prop]) {
return false;
}
} else {
return false;
}
}
return true;
}
实现 call
javascript
Function.prototype._call = function (context, ...args) {
context = context ?? window;
const funcSymbol = Symbol();
context[funcSymbol] = this;
const result = context[funcSymbol](...args);
delete context[funcSymbol];
return result;
}
实现 bind
javascript
Function.prototype.bind = function (context, ...args) {
if(typeof this !== 'function') throw new TypeError();
const func = this;
return function AnonymousFunc(...restArgs) {
if(this instanceof AnonymousFunc) {
return new func(...args, ...restArgs);
}
return func.call(context, ...args, ...restArgs);
}
}
实现数组降维
typescript
const flat1 = (array: any[]) => {
const result = [];
while(array.length) {
const back = array.pop();
if(Array.isArray(back)) {
array.push(...back);
} else {
result.unshift(back);
}
}
return result;
}
const flat2 = (array: any[], res = []) => {
for(let item of array) {
if(Array.isArray(item)) {
flat2(item, res);
} else {
res.push(item);
}
}
return res;
}
const flat3 = (array: any[], depth: number = Infinity, res = [] ) => {
if(depth!== Infinity && depth < 0) {
res.push(array);
return;
}
for(let item of array) {
if(Array.isArray(item)) {
flat3(item, depth - 1, res);
} else {
res.push(item);
}
}
return res;
}
中间件
javascript
const middleware = [];
middleware.push((next) => {
console.log(11);
next();
next();
console.log(22);
})
middleware.push((next) => {
console.log(33);
next();
console.log(44);
})
middleware.push((next) => {
console.log(55);
})
middleware.push((next) => {
console.log(66);
})
function doMiddleWare(middles) {
return function () {
let index = -1;
function dispatch() {
index++;
if(index > middles.length - 1) {
return;
}
let execFunc = middles[index];
try {
return Promise.resolve(execFunc(dispatch));
} catch (err) {
return Promise.reject(err);
}
}
return dispatch();
}
}
doMiddleWare(middleware)();
// 11 33 55 44 66 22