简单聊聊大家所常说的浅拷贝、深拷贝

数据存储

在我们了解了js的数据类型之后,接下来我们便需要重点考虑数据的存储问题了,因为其在各大框架中都是很重要的。

基本数据类型因为占用内存较小因而存储在栈内存中,同时当基本类型的变量互相赋值时,不会出现值共享问题。

而引用数据类型因为存储在堆内存中,通过一个存储在栈中的引用(即内存地址)指向其在堆内存中的空间,因此互相赋值的时候便会出现引用值共享问题,那么如何解决这个问题呢?其实只要搞懂了原理不就可以解决这个问题了吗。

浅拷贝

let arr = [1, 2, 3];
let newArr = arr;
newArr[0] = 100;

console.log(arr);//[100, 2, 3]

这是直接赋值的情况,不涉及任何拷贝,当改变newArr的时候,由于指向的是同一个引用,因此arr值也会跟着改变(也就是上面提到的值共享问题)

现在便来说说大家所常说的浅拷贝:

let arr = [1, 2, 3];
let newArr = arr.slice();//原理就是将原数组的值拷贝一份放入一个新的数组中并返回
newArr[0] = 100;

console.log(arr);//[1, 2, 3]

此时便可以发现,当修改newArr的时候,arr的值并不改变,因为这里的newArr是浅拷贝arr之后的结果,newArr和arr现在引用的已经不是同一块内存空间了,而这就是大家所常说的浅拷贝。

接下来,我们来研究一下js中到底有多少种方式去实现浅拷贝?到这里其实你应该已经搞清了原理,那方法不就挺多的吗。

1,使用ES6的展开运算符。

2,使用slice方法。

3,使用concat方法

let arr = [1, 2, 3];
let newArr = arr.concat();
newArr[1] = 100;
console.log(arr);//[ 1, 2, 3 ]

4,我们也可以手动实现浅拷贝

function shallowClone(target) {
    if (typeof target === "object" && target !== null) {
        //第一步:根据是普通对象还是数组对象创建一个空的对象
        let cloneTarget = Array.isArray(target) ? [] : {};

        //第二步:利用循环将所有元素拷贝一份
        for (let i = 0; i < target.length; i++) {
            if (target.hasOwnProperty(i)) {
                cloneTarget.push(target[i]);
            }
        }
        return cloneTarget;
    } else {
        return target;
    }
}

//我们来测试一下
let arr = [1, 2, 3];
let newArr = shallowClone(arr);
console.log(arr, newArr);
newArr[0] = 10;
console.log(arr, newArr);

5,其实也可以使用 Object.assign 方法(react中的setState原理其实就是利用的这个方法)

let obj = {
    a: '1'
}
let newObj = Object.assign({}, obj, { a: '2' });
console.log(obj, newObj);

所以通过上面的几种方法,我们可以发现它们其实使用的都是同一种原理:即通过遍历或者拷贝等方式拿到原数组的所有元素并放入一个新的数组中,最后返回(这不就相当于重新开辟了一块新的内存空间吗)

深拷贝

注意:这里只解决对象嵌套以及循环引用的问题。

学习深拷贝之前,我们先来看下面一段代码:

let arr = [1, 2, {val: 4}];
let newArr = arr.slice();
newArr[2].val = 1000;

console.log(arr);//[ 1, 2, { val: 1000 } ]

在这里你会发现为什么改变了newArr下标为2的元素的val值,arr下标为2的元素的val值也跟着变了,这是为什么呢?这就是浅拷贝的限制所在了:它只能拷贝一层对象,如果有对象的嵌套,那么浅拷贝将无能为力,但幸运的是,深拷贝就是为了解决这个问题而生的,它能解决无限级的对象嵌套问题,实现彻底的拷贝。

JSON.parse(JSON.stringify());

估计这个api能覆盖大多数的应用场景,比如:上面对象嵌套的问题,没错,谈到深拷贝,我第一个想到的也是它,但是实际上,对于某些严格的场景来说,这个方法是有巨大的坑的,比如:无法解决循环引用的问题,举个例子:

const a = {val:2};
a.target = a;//循环引用

那么此时拷贝a便会出现系统栈溢出,因为出现了无限递归调用的情况,因此这个api先pass掉,我们重新写一个可以解决循环引用问题的深拷贝:首先创建一个Map记录下已经拷贝过的对象,如果说已经拷贝过,那直接返回它就行了。

function deepClone(target, map = new WeakMap()) {
    const isObject = (target) => {
        (typeof target === "object" || typeof target === "function") &&
            target !== null;
    };

    if (isObject(target)) {
        map.set(target, true);
        let cloneTarget = Array.isArray(target) ? [] : {};

        for (let i = 0; i < target.length; i++) {
            if (target.hasOwnProperty(i)) {
                cloneTarget.push(deepClone(target[i], map));
            }
        }
        return cloneTarget;
    } else {
        return target;
    }
}

//我们来测试一下是否可以解决循环引用的问题
let obj = {
    name: "kobe",
};

obj.a = obj;
let newObj = deepClone(obj);
console.log(newObj);

好像是没有问题了, 拷贝也完成了,但还是有一个潜在的坑,就是map 上的 key 和 map 构成了强引用关系,这是相当危险的,被弱引用的对象可以在任何时候被回收 ,而对于强引用来说,只要这个强引用还在,那么对象在程序结束之前便不会被释放回收。怎么解决这个问题呢?很简单,让 map 的 key 和 map 构成弱引用即可,ES6给我们提供了这样的数据结构,它的名字叫weakMap,它是一种特殊的Map, 其中的键是弱引用的。其键必须是对象,而值可以是任意的。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
红黑树是一种在2-3树基础上发展而来的自平衡二叉搜索树。它的特点是每个节点都有一个颜色属性,可以是红色或黑色。红黑树满足以下性质: 1. 每个节点要么是红色,要么是黑色。 2. 根节点是黑色。 3. 每个叶子节点(NIL节点,空节点)是黑色。 4. 如果一个节点是红色的,则它的两个子节点都是黑色的。 5. 对于每个节点,从该节点到其所有后代叶子节点的简单路径上,均包含相同数目的黑色节点。 这些性质保证了红黑树的平衡性和高效性。红黑树的平衡性是通过对节点进行颜色调整和旋转操作来实现的。这些操作可以保持树的高度在O(log n)范围内,从而保证了树的查找、插入和删除操作的时间复杂度都是O(log n)。 红黑树的应用非常广泛,特别是在高级语言的编译器和标准库中。它常被用作实现有序集合、映射和优先队列等数据结构。红黑树的特性使得它在插入和删除操作频繁的情况下仍能保持较好的性能。 如果你对红黑树还不太了解,建议先学习2-3树的基础知识,因为红黑树是在2-3树的基础上发展而来的。了解2-3树的结构和操作可以帮助你更好地理解红黑树的原理和实现。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* *2* *3* [面试让我手写红黑树?!](https://blog.csdn.net/Yao__Shun__Yu/article/details/127206776)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v4^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值