资讯专栏INFORMATION COLUMN

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

ccj659 / 1612人阅读

摘要:前端芝士树浅拷贝深拷贝以及的作用首先还是得回到的基本数据类型。值类型深拷贝数值布尔值字符串。它接受任意数量的源对象,主要作用就是枚举它们的所有属性并分配给。

【前端芝士树】浅拷贝、深拷贝以及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}

所以深拷贝问题的出现就是为了解决引用类型的数据的浅拷贝特性

实现对象深拷贝的几种方法

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 和其他内置类型在进行序列化时会丢失。

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()方法属于深拷贝吗?

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

对于一维数组而言

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

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://www.ucloud.cn/yun/102812.html

相关文章

  • 拷贝拷贝

    摘要:二浅拷贝与深拷贝深拷贝和浅拷贝是只针对和这样的引用数据类型的。浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。对于字符串数字及布尔值来说不是或者对象,会拷贝这些值到新的数组里。 一、数据类型 数据分为基本数据类型(String, Number, Boolean, Null, Undefined,Symbol)和对象数据类型。 基本数据类型的特点:直...

    hzc 评论0 收藏0
  • 拷贝拷贝

    摘要:二浅拷贝与深拷贝深拷贝和浅拷贝是只针对和这样的引用数据类型的。浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。对于字符串数字及布尔值来说不是或者对象,会拷贝这些值到新的数组里。 一、数据类型 数据分为基本数据类型(String, Number, Boolean, Null, Undefined,Symbol)和对象数据类型。 基本数据类型的特点:直...

    史占广 评论0 收藏0
  • JS拷贝

    摘要:引用类型之所以会出现深浅拷贝的问题,实质上是由于对基本类型和引用类型的处理不同。另外方法可以视为数组对象的浅拷贝。上面描述过的复杂问题依然存在,可以说是最简陋但是日常工作够用的深拷贝方式。 一直想梳理下工作中经常会用到的深拷贝的内容,然而遍览了许多的文章,却发现对深拷贝并没有一个通用的完美实现方式。因为对深拷贝的定义不同,实现时的edge case过多,在深拷贝的时候会出现循环引用等问...

    xiaoxiaozi 评论0 收藏0
  • ES6时代,你真克隆对象吗?

    摘要:原文你真的会克隆对象吗开始之前在开始聊克隆之前,我们还是先来看看数据类型。值通过函数生成,是独一无二的。同时,中规定了对象的属性名有两种类型,一种是字符串,另一种就是类型。返回一个数组,包含对象自身的所有属性的键名。 原文:你真的会克隆对象吗 开始之前 在开始聊克隆之前,我们还是先来看看js数据类型。js的数据类型分为基本数据类型和复杂数据类型。 基本数据类型:Number、Bool...

    xiaokai 评论0 收藏0
  • 拷贝拷贝区别

    摘要:浅拷贝与深拷贝一数据类型数据分为基本数据类型,和对象数据类型。浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。对于字符串数字及布尔值来说不是或者对象,会拷贝这些值到新的数组里。 浅拷贝与深拷贝 一、数据类型数据分为基本数据类型(String, Number, Boolean, Null, Undefined,Symbol)和对象数据类型。 基本数据类...

    jsyzchen 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<