9012年,当我们讨论js深浅拷贝时我们在说些什么?

栏目: JavaScript · 发布时间: 6年前

内容简介:正文:讨论深浅拷贝,首先要从js的基本数据类型说起: 根据 JavaScript 中的变量类型传递方式,分为值类型和引用类型, 值类型变量包括 Boolean、String、Number、Undefined、Null。引用类型包括了 Object 类的所有, 如 Date、Array、Function 等。在参数传递方式上,值类型是按值传递,引用类型是按地址传递。我们来看一下这两则有什么区别:
9012年,当我们讨论js深浅拷贝时我们在说些什么?
前言:
本文主要阅读对象:对深浅拷贝印象模糊对初级前端,想对js深浅拷贝聊一聊的中级前端。
 如果是对这些有完整对认知体系和解决方法对大佬,可以不用再浪费时间了
复制代码

正文:讨论深浅拷贝,首先要从js的基本数据类型说起: 根据 JavaScript 中的变量类型传递方式,分为值类型和引用类型, 值类型变量包括 Boolean、String、Number、Undefined、Null。

引用类型包括了 Object 类的所有, 如 Date、Array、Function 等。在参数传递方式上,值类型是按值传递,引用类型是按地址传递。

我们来看一下这两则有什么区别:

//eg1:
// 值类型
var a = 10
var b = a
b = 20
console.log(a)  // 10
console.log(b)  // 20

//解析:上述代码中,a b都是值类型,两者分别修改赋值,相互之间没有任何影响。再看引用类型的例子:

//eg2
// 引用类型
var a = {x: 10, y: 20}
var b = a
b.x = 100
b.y = 200
console.log(a)  // {x: 100, y: 200}
console.log(b)  // {x: 100, y: 200}
复制代码

解析: 上述代码中,a b都是引用类型。在执行了b = a之后,修改b的属性值,a的也跟着变化。因为a和b都是引用类型,指向了同一个内存地址,即两者引用的是同一个值,因此b修改属性时,a的值随之改动。

**那到底什么是深浅拷贝呢?**
<br />解析:深浅拷贝是拷贝对象的`深度`来说的:
<br />当你想拷贝的对象只有一级时,就是浅拷贝。
<br />当你拷贝的对象有多级的时候,就是深拷贝。
复制代码

再回到上面的那个例子: 在例子能看出来,如果直接采用“=”赋值,这种类型,当我们改变b的值时候,a也会随之改变的,假如不允许改变a呢? 这时,深拷贝就出场了。

function clone(val){
    if(!val && typeof val !== 'object'){
        return
    }

    const newArr = toString.call(val) === ['object Array'] ? [] : {}

    for (let item in val) {
        if(typeof val[item] ===  'object') {
            newArr[item] = clone(item)
        }else {
            newArr[item] = val[item]
        }
    }
    return newArr
 }

//测试:
    var a = {x: 10, y: 20}
    var b = clone(a)
    b.x = 100
    b.y = 200
    console.log(a)  // {x: 10, y: 20}
    console.log(b)  //{x: 100, y: 200}

复制代码

解析:对于这个深拷贝,大家看到这里应该都可以看明白:主要思路就是浅拷贝 + 递归。

这有几个点需要注意:

1. 判断接收到的参数类型。

2. 使用toString.call()判断更加严谨。

3. 考虑对其他引用类型的数据兼容(这里并没有对每种类型的数据组做判断,引用类型包括了 Object 类的所有,如 Date、Array、Function 等。)

需要注意的是:用递归的话,会有一下几个需要特别注意的点:
    1. 当数据层次很深时,容易爆栈(栈溢出) 解决办法=>
        a. 消除尾递归
        b. 改用循环

    2. “循环引用”,会导致死循环  解决办法 =>
        a. 循环检测
        b. 暴力破解

第一种:数据广度特别大,层次特别深
第二种:类似下面的这种情况
    var a = {}
    a.a = a
    clone(a) //死循环
    

ES6中一行代码实现深拷贝:JSON.parse(JSON.stringify(source))
注释:JSON.stringify()其实利用了“循环检测”机制
复制代码

这里给大家推荐一篇循环解决递归的方法:可以保持拷贝数据以后的引用关系

function cloneForce(x) {
    const uniqueList = []; // 用来去重

    let root = {};

    // 循环数组
    const loopList = [
        {
            parent: root,
            key: undefined,
            data: x,
        }
    ];

    while(loopList.length) {
        // 深度优先
        const node = loopList.pop();
        const parent = node.parent;
        const key = node.key;
        const data = node.data;

        // 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素
        let res = parent;
        if (typeof key !== 'undefined') {
            res = parent[key] = {};
        }
        
        // 数据已经存在
        let uniqueData = find(uniqueList, data);
        if (uniqueData) {
            parent[key] = uniqueData.target;
            continue; // 中断本次循环
        }

        // 数据不存在
        // 保存源数据,在拷贝数据中对应的引用
        uniqueList.push({
            source: data,
            target: res,
        });
    
        for(let k in data) {
            if (data.hasOwnProperty(k)) {
                if (typeof data[k] === 'object') {
                    // 下一次循环
                    loopList.push({
                        parent: res,
                        key: k,
                        data: data[k],
                    });
                } else {
                    res[k] = data[k];
                }
            }
        }
    }

    return root;
}

function find(arr, item) {
    for(let i = 0; i < arr.length; i++) {
        if (arr[i].source === item) {
            return arr[i];
        }
    }

    return null;
}

//eg1:
var a = {
    a1: b,
    a2: b,
}

var b = {};

a.a1 === a.a2 // true

var c = cloneForce(a);
c.a1 === c.a2 // true   引用保持一致

//eg2:
var a = {};
a.a = a;

console.log(cloneForce(a))//还可以破解循环引用
复制代码

主要思路是: 声明一个数组对象(父对象,key,value),其有值时,取其最后一个对象; 判断key有值,代表当前级下有子级,则拷贝到子元素。如果数据已经存在,则中断本次循环。数据不存在则对其拷贝。

有一点需要注意:cloneForce在对象数量很多时会出现很大的问题,如果数据量很大不适合使用cloneForce。

有兴趣的同学,可以前往 深拷贝的终极探索(90%的人都不知道) ,查看更多的细节。

总结:如果觉得对你有帮助,请给作者一点小小的鼓励, 点个赞或者收藏 吧。 有需要沟通的请联系我: 微信( wx9456d )邮箱( allan_liu986@163.com )


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Chinese Authoritarianism in the Information Age

Chinese Authoritarianism in the Information Age

Routledge / 2018-2-13 / GBP 115.00

This book examines information and public opinion control by the authoritarian state in response to popular access to information and upgraded political communication channels among the citizens in co......一起来看看 《Chinese Authoritarianism in the Information Age》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

在线进制转换器
在线进制转换器

各进制数互转换器

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试