【前端芝士树】浅拷贝、深拷贝以及Object.assign()的作用、克隆对象、复制数组

【前端芝士树】浅拷贝、深拷贝以及Object.assign()的作用

起首照样得回到Javascript的基础数据范例。

值范例[深拷贝]:数值Num、布尔值Boolean、字符串String、null、undefined。

基础范例值是指在栈内存保存的简朴数据段,在复制基础范例值的时刻,会拓荒出一个新的内存空间,将值复制到新的内存空间,举个栗子:

var a = 1;
var b = a;
a = 2;
console.log(a);//输出2;
console.log(b);//输出1;

援用范例[浅拷贝]:对象、数组、函数等。

用范例值是保存在堆内存中的对象,变量保存的只是指向该内存的地点,在复制援用范例值的时刻,实在只复制了指向该内存的地点,举个栗子:

var a={b:1}
var a2 = a;
a2.b = 2;
console.log(a)  // 输出 {b: 2}

所以深拷贝题目的涌现就是为了处置惩罚援用范例的数据的浅拷贝特征

完成对象深拷贝的几种要领

  1. JSON.parse() && JSON.stringfy()
    将该对象转换为其 JSON 字符串示意情势,然后将其剖析回对象。这觉得有点太甚简朴了,但它确切有用:

    const obj = /* ... */;
    const copy = JSON.parse(JSON.stringify(obj));

    长处是,假如没有轮回对象,而且不须要保存内置范例,运用该要领皆能够获得最快的跨浏览器的克隆机能。
    这里的瑕玷是创建了一个暂时的,能够很大的字符串,只是为了把它从新放回剖析器。
    另一个瑕玷是这类要领不能处置惩罚轮回对象,而且轮回对象常常发作。
    比方,当我们构建树状数据结构,个中一个节点援用其父级,而父级又援用其子级。

    const x = {};
    const y = {x};
    x.y = y; // Cycle: x.y.x.y.x.y.x.y.x...
    const copy = JSON.parse(JSON.stringify(x)); // throws!

    别的,诸如 Map, Set, RegExp, Date, ArrayBuffer 和其他内置范例在举行序列化时会丧失。

  2. MessageChannel && postMessage 结构化克隆算法
    这类要领的瑕玷是它是异步的。虽然这并没有大碍,然则有时刻你须要运用同步的体式格局来深度拷贝一个对象。

    function structuralClone(obj) {
      return new Promise(resolve => {
        const {port1, port2} = new MessageChannel();
        port2.onmessage = ev => resolve(ev.data);
        port1.postMessage(obj);
      });
    }
    
    const obj = /* ... */;
    const clone = await structuralClone(obj);

Array.slice()Array.concat()要领属于深拷贝吗?

这个我都被弄糊涂了,网上找了些材料才捋清了一下。

关于一维数组而言

  1. arrayObj.slice(start, [end])

    var arr1 = ["1","2","3"];
    var arr2 = arr1.slice(0);
    arr2[1] = "9";
    console.log("数组的原始值:" + arr1 ); //1,2,3
    console.log("数组的新值:" + arr2 ); //1,9,3
  2. arrayObj.concat(arr1,arr2 … )

    var arr1 = ["1","2","3"];
    var arr2 = arr1.concat();
    arr2[1] = "9";
    console.log("数组的原始值:" + arr1 ); //1,2,3
    console.log("数组的新值:" + arr2 );//1,9,3

那数组内里假如包括对象呢?

var arr1 = [{"name":"weifeng"},{"name":"boy"}];//原数组
var arr2 = [].concat(arr1);//拷贝数组
arr1[1].name="girl";
console.log(arr1);// [{"name":"weifeng"},{"name":"girl"}]
console.log(arr2);//[{"name":"weifeng"},{"name":"girl"}]

var a1=[["1","2","3"],"2","3"],a2;
a2=a1.slice(0);
a1[0][0]=0; //转变a1第一个元素中的第一个元素
console.log(a2[0][0]);  //影响到了a2

从上面两个例子能够看出,因为数组内部属性值为援用对象,因而运用slice和concat对对象数组的拷贝,全部拷贝照样浅拷贝,拷贝以后数组各个值的指针照样指向雷同的存储地点。

Array.slice()
Array.concat() 这两个要领,仅适用于对不包括援用对象的一维数组的深拷贝!

Object.assign() 要领 以及 对象扩大操作符 ...

Object.assign() 要领

Object.assign()考核点是ES6中完成对象复制,关于Object.assign()这个函数这里有一篇文章讲得异常细致邃晓。

ES6供应了Object.assign(),用于兼并/复制对象的属性。

Object.assign(target, source_1, ..., source_n)

下面是一个例子

var o1 = { a: 1, b: 1, c: 1 };
var o2 = { b: 2, c: 2 };
var o3 = { c: 3 };

var obj = Object.assign({}, o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }

那末Object.assign()要领是浅拷贝照样深拷贝呢?请看下面这个例子:

function mutateDeepObject(obj) {
  obj.a.thing = true;
}

const obj = {a: {thing: false}};
const copy = Object.assign({}, obj);
mutateDeepObject(copy)
console.log(obj.a.thing); // prints true 

Object.assign(target, sources...)是一个简朴的拷贝对象的体式格局,属于浅拷贝。它接收恣意数目的源对象,重要作用就是罗列它们的一切属性并分配给
target

对象扩大操作符 ...

运用对象扩大操作符 …,对象本身的可罗列属性能够被拷贝到新对象。

const obj = { a: 1, b: 2, c:{d:'d'} }
const shallowClone = { ...obj }
shallowClone.a = 'a';
shallowClone.c.d = '4';
console.log(obj); // a: 1, b: 2, c: {d: "4"}}
console.log(shallowClone); // a: "a", b: 2, c: {d: "4"}}

其他的看上去像是对象深拷贝,本质是浅拷贝的要领

const obj = { a: 1, b: 2, c:{d:'d'} }
const shallowClone = Object.keys(obj).reduce((acc, key) => (acc[key] = obj[key], acc), {});
shallowClone.a = 'a';
shallowClone.c.d = '4';
console.log(obj); // a: 1, b: 2, c: {d: "4"}}
console.log(shallowClone); // a: "a", b: 2, c: {d: "4"}}
    原文作者:云中的猫
    原文地址: https://segmentfault.com/a/1190000018609149
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞