拷贝 - 比较(回顾)

浅拷贝

定义:只把第一级的拷贝一份,赋值给新数组,一般我们实现数组克隆的方法都是
方法:slice,concat,[…ary] 等

var a = { name: 1 };
var arr = [a];
var temp = arr.slice();
console.log(temp); // [{name:2}]   为什么这里name不是 1,控制台的异步
a.name = 2;
console.log(temp); //[{name:2}]

深拷贝

定义:把每一级都克隆一份给新数组.

使用 JSON

实现原理:

JSON.stringify(arr) 先把原始对象变为字符串(去除堆和嵌套关系)
JSON.parse(…) 再把字符串转换为新对象,重新开辟内存存储

存在问题:

  • undefined函数symbol会被忽略
  • Number,String,Boolean类型会被转成初始值
  • 不可枚举属性会被忽略
  • 正则变成空对象,日期对象变成字符串
var a = {
    name: 1,
    f: () => "Fn",// 会被忽略
    u: undefined,// 会被忽略
    s: Symbol.for("s"),// 会被忽略
    num:new Number(1),//会被转成初始值
    str: new String("1"),//会被转成初始值
    bool: new Boolean("1"),//会被转成初始值
    reg: /a+/g,// 会被转成空对象
    d: new Date(),//会被转成字符串
};
Object.defineProperty(a, "e", {
    value: 1,
    enumerable: false
})
var arr = [a];
var temp = JSON.parse(JSON.stringify(arr));
a.name = 2;
console.log(temp);
// [
//     {
//         name: 1,
//         num: 1,
//         str: '1',
//         bool: true,
//         reg: {},
//         d: '2023-04-21T01:26:46.845Z'
//     }
// ]

递归实现深拷贝

实现思路

  1. 考虑 null,基本类型
  2. 考虑 Date,正则对象
  3. 考虑是否为自己的私有属性
  4. 递归
function clone(obj) {
    if (obj === null) return obj
    if(typeof obj !== 'object') return obj
    if (obj instanceof Date) return new Date(obj)
    if (obj instanceof RegExp) return new RegExp(obj)
    const temp = new obj.constructor()
    for (let key in obj) {
        if (!obj.hasOwnProperty(key)) break
        temp[key] = clone(obj[key])
    }
    return temp
}
var a = {
    name: 1,
    f: () => "Fn",
    u: undefined,
    s: Symbol.for("s"),
    num: new Number(1),
    str: new String("1"),
    bool: new Boolean("1"),
    reg: /a+/g,
    d: new Date(),
};
Object.defineProperty(a, "e", {
    value: 1,
    enumerable: false
})
let res = clone({a})
a.name = 3
console.log(res)
// {
//     a: {
//         name: 1,
//         f: [Function: f],
//         u: undefined,
//         s: Symbol(s),
//         num: [Number: 0],
//         str: [String: ''] { '0': '1' },
//         bool: [Boolean: false],
//         reg: /a+/g,
//         d: 2023-04-23T06:49:38.432Z
//     }
// }

实现思路

  1. 考虑 Symbol 属性
  2. 考虑 循环引用 造成的内存泄漏(weakMap)
  3. 考虑 对象属性 和 继承原型
function clone(obj,hash = new WeakMap()) {
    if (obj === null) return obj
    if(typeof obj !== 'object') return obj
    if (obj instanceof Date) return new Date(obj)
    if (obj instanceof RegExp) return new RegExp(obj)
    // 处理循环引用
    if(hash.has(obj))return hash.get(obj)
    // 处理描述属性
    let allDesc = Object.getOwnPropertyDescriptor(obj)
    // 处理原型
    let temp = Object.create(Object.getPrototypeOf(obj),allDesc)
    hash.set(obj,temp)
    for (let key of Reflect.ownKeys(obj)) {
        temp[key] = clone(obj[key],hash)
    }
    return temp
}
Object.prototype.aaa = 1
let obj = {
    num: 0,
    str: '',
    boolean: true,
    unf: undefined,
    nul: null,
    obj: { name: '我是一个对象', id: 1 },
    arr: [0, 1, 2],
    func: function () { console.log('我是一个函数') },
    date: new Date(0),
    reg: new RegExp('/我是一个正则/ig'),
    [Symbol('1')]: 1,
};
Object.defineProperty(obj, 'innumerable', {
    enumerable: false, value: '不可枚举属性' }
);
obj = Object.create(obj, Object.getOwnPropertyDescriptors(obj))
obj.loop = obj    // 设置loop成循环引用的属性
let res = clone(obj)
console.log(res)
// {
//     num: 0,
//     str: '',
//     boolean: true,
//     unf: undefined,
//     nul: null,
//     obj: { name: '我是一个对象', id: 1 },
//     arr: Array { '0': 0, '1': 1, '2': 2, length: 3 },
//     func: [Function: func],
//     date: 1970-01-01T00:00:00.000Z,
//     reg: /\/我是一个正则\/ig/,
//     loop: [Circular *1],
//     [Symbol(1)]: 1
// }

浅比较

实现思路:

  1. 先比较成员的数量
  2. 在逐一比较内部成员(只比较第一层)
  3. 比较内部成员,是否都有这个成员,且是否相同(考虑 NaN )
const shallowEqual = function (objA, objB) {
    if (!isObject(objA) || !isObject(objB)) return false
    if (objA === objB) return true
    // 先比较成员的数量
    let keysA = Reflect.ownKeys(objA)
    let keysB = Reflect.ownKeys(objB)
    if (keysA.length !== keysB.length) return false
    // 逐一比较内部成员(值比较第一层)
    for (let i = 0; i < keysA.length; i++) {
        let key = keysA[i];
        // 如果一个对象中有这个成员,一个对象中没有;或者,都有这个成员,但是成员值不一样,都应该被判定为为不相同
        if (!objB.hasOwnProperty(key) || !Object.is(objA[key], objB[key])) {
            return false
        }
    }
    return true
    function isObject(obj) { // 监测是否为对象
        return obj !== null && /^(object|function)$/.test(typeof obj)
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柳晓黑胡椒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值