【JavaScript】你了解深浅克隆吗

写在前面

数组的深浅克隆在前端开发中经常使用,面试也经常被问到,本篇文章将详细介绍深浅克隆。

深浅克隆

深浅克隆的定义和区别

1、浅克隆: 只拷贝一层对象的属性,当一个属性的值为基本类型值时,就会正常拷贝,且改变新对象,不会改变原对象中的这个值;当一个属性的值为引用类型值时,拷贝的就是地址,当改变这其中值的时候,也同时会改变原对象中的值。

2、深克隆: 深拷贝则不同,它是将对象中的每一层都拷贝到新对象上,每个属性都指向了不同的地址,这样,无论怎么修改新对象,都不会改变原对象的值

实现深浅克隆

实现浅克隆
  • 展开运算符(…)

var arr = [{name: '数组', action: '浅克隆', obj: {name: "对象"}}, 1, 2];

var brr = [...arr];

console.log(arr, brr);  // [{name: '数组', action: '浅克隆', obj: {name: "对象"}}, 1, 2] [{name: '数组', action: '浅克隆', obj: {name: "对象"}}, 1, 2]

console.log(arr === brr);  // false

// 1.改变数组中第二项的值,这个值为基本类型值
brr[1] = '改变'

console.log(arr, brr);  // [{...}, 1, 2]  [{...}, '改变', 2]

// 2.改变数组中第一项中的name值,数组第一项为引用类型值
brr[0].name = "对象";

console.log(arr, brr);  // [{name: '对象', ...}, 1, 2] [{name: '对象', ...}, 1, 2]
  • Object.assign()

var arr = [{name: '数组', action: '浅克隆', obj: {name: "对象"}}, 1, 2];

var brr = Object.assign([], arr)

// 1.改变数组中第二项的值,这个值为基本类型值
brr[1] = '改变'

console.log(arr, brr);  // [{...}, 1, 2]  [{...}, '改变', 2]

// 2.改变数组中第一项中的name值,数组第一项为引用类型值
brr[0].name = "对象";

console.log(arr, brr);  // [{name: '对象', ...}, 1, 2] [{name: '对象', ...}, 1, 2]
  • Array.slice()

var arr = [{name: '数组', action: '浅克隆', obj: {name: "对象"}}, 1, 2];

var brr = arr.slice(0)

console.log(arr, brr); // [{...}, 1, 2] [{...}, 1, 2]

// 1.改变数组中第二项的值,这个值为基本类型值
brr[1] = 5;

console.log(arr, brr); // [{...}, 1, 2] [{...}, 5, 2]

// 2.改变数组中第一项中的name值,数组第一项为引用类型值
brr[0].name = "对象";

console.log(arr, brr);  // [{name: '对象', ...}, 1, 2] [{name: '对象', ...}, 1, 2]
  • Array.from()

var arr = [{name: '数组', action: '浅克隆', obj: {name: "对象"}}, 1, 2];

var brr = Array.from(arr);

console.log(arr, brr); // [{...}, 1, 2] [{...}, 1, 2]

// 1.改变数组中第二项的值,这个值为基本类型值
brr[1] = 5;

console.log(arr, brr); // [{...}, 1, 2] [{...}, 5, 2]

// 2.改变数组中第一项中的name值,数组第一项为引用类型值
brr[0].name = "对象";

console.log(arr, brr);  // [{name: '对象', ...}, 1, 2] [{name: '对象', ...}, 1, 2]
实现深克隆
  • JSON.stringify()和JSON.parse()组合

var arr = [{name: '数组', action: '浅克隆', obj: {name: "对象"}}, 1, 2];

var brr = JSON.parse(JSON.stringify(arr));

console.log(arr, brr); // [{...}, 1, 2] [{...}, 1, 2]

brr[0].name = '对象';

console.log(arr, brr); // [{name: '数组', ...}, 1, 2] [{name: '对象', ...}, 1, 2]

上面我们实现了深克隆,但是这种方法有一个弊端,它在克隆过程中会忽略正则、日期、Symbol、undefined和循环引用对象等

var json = {name: '数组', reg: /^[0-9]$/}

console.log(JSON.parse(JSON.stringify(json)));  // { name: "数组", reg: {} }

看到结果,你会发现,正则表达式在深克隆之后变成了空对象,所以这个方法是有一定的弊端的,下面,我们自己来写一个深克隆方法~

  • 自己实现

1、第一种方法

// 判断类型函数
function _type(data) {
    return Object.prototype.toString.call(data);
}
// 深克隆
function _deepClone(obj) {
    // 判断传入数据类型
    if(obj === null) return null;

    if(typeof obj !== "object") return obj;

    if(_type(obj) === "[object RegExp]") return new RegExp(obj);

    if(_type(obj) === "[object Date]") return new Date(obj);
    // 创建对象
    let newObj = new obj.constructor;

    for(let key in obj) {
        if(!obj.hasOwnProperty(key)) break;

        // 只要是私有属性就递归调用
        newObj[key] = _deepClone(obj[key])
    }
    return newObj
}

// 测试
var json = {name: '数组', reg: /^[0-9]$/}

var newJson = _deepClone(json);
console.log(json, newJson)  // { name: "数组", reg: /^[0-9]$/ }  { name: "数组", reg: /^[0-9]$/ }

newJson.name = '对象';

console.log(json, newJson); // { name: "数组", reg: /^[0-9]$/ } { name: "对象", reg: /^[0-9]$/ }

2、第二种方法

function _type(data) {
    return Object.prototype.toString.call(data)
}

function _deepClone(obj) {
    let newObj = new obj.constructor;
    for(let key in obj) {
        let item = obj[key],
            itemType = _type(obj[key]);
        if(!obj.hasOwnProperty(key)) break;
        // 只有当前属性对应值不是nullq且为“object”时,才递归
        if(obj !== null && typeof item === "object") {
            // 如果值为正则,日期类型,需要特殊处理
            if(/(RegExp|Date)/.test(itemType)) {
                newObj[key] =  new obj.constructor(item);
                continue;
           }
           newObj[key] = _deepClone(item);
           continue;
        }
        newObj[key] = item;
    }
    return newObj;
}

// 测试
var json = {name: '数组', reg: /^[0-9]$/, u: undefined, s: Symbol('111')}

var newJson = _deepClone(json);
console.log(json, newJson)  // { name: "数组", reg: /^[0-9]$/, u: undefined, s: Symbol(111) }  { name: "数组", reg: /^[0-9]$/, u: undefined, s: Symbol(111) }

newJson.name = '对象';

console.log(json, newJson); // { name: "数组", reg: /^[0-9]$/, u: undefined, s: Symbol(111) } { name: "对象", reg: /^[0-9]$/, u: undefined, s: Symbol(111) }

面试官问:你了解深浅克隆吗?

相信大家在面试中都被问过这个问题,那么你答得怎么样?我们可以从以下几个角度按照顺序回答这个问题:

  • 深浅克隆的定义

  • 深浅克隆的区别

  • 深浅克隆如何实现

  • 深浅克隆在工作中的应用

如果大家有更好的想法回答这个问题,欢迎在评论区交流~

写在最后

最后,欢迎大家关注我的公众号「web前端日记」,关注有福利领取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值