likes
comments
collection
share

实现深浅拷贝

作者站长头像
站长
· 阅读数 31

今天累了,写个简单的文章来凑个数吧,实现深浅拷贝,面试必备

实现深浅拷贝

浅拷贝

  • Object.assign
  • 扩展运算符 let obj2 = { ...obj1 }
  • 自己简单实现
function isObj(obj) {
    return typeof obj === 'object' && obj !== null;
}
// 遍历赋值
function clone(obj) {
    if(!isOjb(obj)) {
        return obj;
    }
    let res = Array.isArray(obj) ? [] : {};
    for(let key in obj) {
        if(Object.hasOwnProperty(key)) {
            res[key] = obj[key];
        }
    }
    return res;
}

深拷贝

  • 通过JSON.stringify实现深拷贝,会有以下问题
  1. 值里面如果有undefined,函数,Symbol,通过stringify序列化后键值都会消失
  2. 无法解决循环引用
  3. 拷贝Date会变字符串,RegExp会变空值
  4. 无法拷贝不可枚举的属性
  5. 无法拷贝对象的原型链
  6. NaN,+ -Infinity变成null

自己实现深拷贝,步骤如下

  1. 先通过遍历对象的属性,实现浅拷贝
export function deepClone(obj) {
    if (!isObject(obj)) {
        return obj;
    }
    const result = {};
    for(let key in obj) {
    if (obj.hasOwnProperty(key)) {
        result[key] = obj[key];
    }
    return result;
}
  1. 兼容数组
export function deepClone(obj) {
    if (!isObject(obj)) {
        return obj;
    }
    const result = Array.isArray(obj) ? [] : {};
    for(let key in obj) {
        if (obj.hasOwnProperty(key)) {
            result[key] = obj[key];
        }
    }
    return result;
}
  1. 加入递归实现深拷贝
export function deepClone(obj) {
    if (!isObject(obj)) {
        return obj;
    }
    const result = Array.isArray(obj) ? [] : {};
    for(let key in obj) {
        if (obj.hasOwnProperty(key)) {
            if (isObject(obj[key])) {
                //*
                result[key] = deepClone(obj[key]);
                //*
            } else {
                result[key] = obj[key];
            }
        }
    }
    return result;
}
  1. 解决循环引用问题(Map 或 WeakMap 或数组)

weakMap可能会有引用丢失的问题,可用Map和数组解决

export function deepClone(obj, weakMap = new WeakMap()) {
    if (!isObject(obj)) {
        return obj;
    }
    if(weakMap.has(obj)) {
        return weakMap.get(obj);
    }
    const result = Array.isArray(obj) ? [] : {};
    weakMap.set(obj, result);
    for(let key in obj) {
        if (obj.hasOwnProperty(key)) {
            if (isObject(obj[key])) {
                result[key] = deepClone(obj[key], weakMap);
            } else {
                result[key] = obj[key];
            }
        }
    }
    return result;
}

数组解决循环引用

function findArr(obj, arr) {
    let res = null;
    for(let item of arr) {
        if(item.source === obj) {
            res = item;
            break
        }
    }
    return res;
}
export function deepClone2(obj, saveArr = []) {
    if(!isObject(obj)) {
        return obj;
    }
    if(findArr(obj, saveArr)) {
        return findArr(obj, saveArr).target;
    }
    const result = Array.isArray(obj) ? [] : {};
    saveArr.push({
        source: obj,
        target: result
    });
    for(let key in obj) {
        if(obj.hasOwnProperty(key)) {
            if(isObject(obj[key])) {
                result[key] = deepClone2(obj[key], saveArr);
            }else {
                result[key] = obj[key];
            }
        }
    }
    return result;
}
  1. 拷贝不可枚举的key和Symbol类型的key,Reflect.ownKeys列出所有的key,包括可枚举不可枚举和Symbol
export function deepClone(obj, weakMap = new WeakMap()) {
    if (!isObject(obj)) {
        return obj;
    }
    if(weakMap.has(obj)) {
        return weakMap.get(obj);
    }
    const result = Array.isArray(obj) ? [] : {};
    weakMap.set(obj, result);
    Reflect.ownKeys(obj).forEach(key => {
        if (obj.hasOwnProperty(key)) {
            if (isObject(obj[key])) {
                result[key] = deepClone(obj[key], weakMap);
            } else {
                result[key] = obj[key];
            }
        }
    });
    return result;
}
  1. 拷贝原型链
export function deepClone(obj, weakMap = new WeakMap()) {
    if (!isObject(obj)) {
        return obj;
    }
    if(weakMap.has(obj)) {
        return weakMap.get(obj);
    }
    // const result = Array.isArray(obj) ? [] : {};
    const descriptors = Object.getOwnPropertyDescriptors(obj)
    const result = Object.create(Object.getPrototypeOf(obj), descriptors);
    weakMap.set(obj, result);
    Reflect.ownKeys(obj).forEach(key => {
        if (obj.hasOwnProperty(key)) {
            if (isObject(obj[key])) {
                result[key] = deepClone(obj[key], weakMap);
            } else {
                result[key] = obj[key];
            }
        }
    });
    return result;
}
  1. 处理特殊对象
export function deepClone(obj, weakMap = new WeakMap()) {
    if (!isObject(obj)) {
        return obj;
    }
    if(weakMap.has(obj)) {
        return weakMap.get(obj);
    }
    // 处理特殊对象 Boolean Number String Error均如此
    if(obj.constructor === Date) {
        return new Date(obj);
    }
    if(obj.constructor === RegExp) {
        return new RegExp(obj);
    }
    // map set类似
    if(obj.constructor === Map) {
        let newTarget = new Map();
        obj.forEach((value, key) => {
            newTarget.set(key, value);
        })
        return newTarget;
    }
    /////////////
    // const result = Array.isArray(obj) ? [] : {};
    const descriptors = Object.getOwnPropertyDescriptors(obj)
    const result = Object.create(Object.getPrototypeOf(obj), descriptors);
    weakMap.set(obj, result);
    Reflect.ownKeys(obj).forEach(key => {
        if (obj.hasOwnProperty(key)) {
            if (isObject(obj[key])) {
                result[key] = deepClone(obj[key], weakMap);
            } else {
                result[key] = obj[key];
            }
        }
    });
    return result;
}
  1. 克隆函数(一般没有必要)

普通函数通过正则匹配, 箭头函数直接用eval(函数toString)

普通函数和箭头函数可通过有没有prototype来区分

function cloneFunc(func) {
    // if(func.protytype) { // 普通函数
    // // 前面是先行断言,后面是后行断言 仅当 ( 后面跟着一个或多个字符,且这一个或多个字符后面跟着 )中间可以多个空格{
    // const paramReg = /(?<=\().+(?=\)\s+{)/;
    // // 前面是先行断言,后面是后行断言 仅当 { 后面跟着一个或多个字符或换行,且这一个或多个字符后面跟着}
    // const bodyReg = /(?<={)(.|\n)+(?=})/m; // m是多行搜索
    // const funcString = func.toString();
    // const param = paramReg.exec(funcString);
    // const body = bodyReg.exec(funcString);
    // if(body) {
    // if(param) {
    // const argList = param[0].split(',');
    // return new Function(...arguments, body[0]);
    // }else {
    // return new Function(body[0]);
    // }
    // }else {
    // return null;
    // }
    // } else { // 箭头函数
    // return eval(func.toString());
    // }
    return eval(`(${func.toString()})`);
}
  1. 最后,如果不用递归来
export function deepClone3(obj) {
    const stack = [];
    const result = {};
    stack.push({
        parent: result,
        key: undefined,
        data: obj
    });
    while(stack.length) {
        console.log('stack', stack);
        let node = stack.pop();
        let parent = node.parent;
        let key = node.key;
        let data = node.data;
        let res = parent;
        if(key !== undefined) {
            res = parent[key] = {};
        }
        Reflect.ownKeys(data).forEach((key) => {
            if(isObject(data[key])) {
                stack.push({
                    parent: res,
                    key: key,
                    data: data[key]
                });
            }else {
                res[key] = data[key];
            }
        });
    }
    return result;
}

注意,面试的时候写到第7点就行

转载自:https://juejin.cn/post/7134724081999413278
评论
请登录