Object.assign()深拷贝 和浅拷贝问题

Object.assign()深拷贝 和浅拷贝问题

objectcreate是深拷贝还是浅拷贝 object的create方法objectcreate是深拷贝还是浅拷贝 object的create方法


objectcreate是深拷贝还是浅拷贝 object的create方法


关于Object.assign()深拷贝 和 浅拷贝的问题一直困扰了我很多,我将从以前几个方面进行总结:

如下例子1所示,更改zy 从 zs上得到,更改zy上的值并不会导致zs的值改变。

总结:Object.assign()对1级属性的拷贝属于神拷贝,对2级对象的拷贝属于浅拷贝。如果要使用深拷贝。可以借助loadsh中的cloneDeep()方法

可变对象的深拷贝与浅拷贝

可变对象为list、dict、set,可变对象可以在其 id() 保持固定的情况下可改变其取值

不可变对象为number、string、tuple,具有固定值的对象。不可变对象包括数字、字符串和元组。这样的对象不能被改变。如果必须存储一个不同的值,则必须创建新的对象

首先深拷贝与浅拷贝针对可变对象的

copy模块

1、浅拷贝会创建一个新的容器对象

2、对于对象中的元素,浅拷贝就只会使用原始元素的引用(内存地址)

1、使用切片作[:] ——a[:]

2、直接赋值 ——a=b

3、copy模块的copy()方法 ——a.copy()

1.深拷贝和浅拷贝一样,都会创建一个新的容器对象(compound object)

2.和浅拷贝的不同点在于,深拷贝对于对象中的元素,深拷贝都会重新生成一个新的对象

1、用copy模块的deepcopy()方法

解构赋值是深拷贝还是浅拷贝?

深拷贝:修改新变量的值不会影响原有变量的值。默认情况下基本数据类型都是深拷贝。

浅拷贝:修改新变量的值会影响原有的变量的值。默认情况下引用类型都是浅拷贝。

ES6的新特性,按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为 解构 。具体作本文章不做描述。

那么我们在使用解构赋值的时候,到底是深拷贝还是浅拷贝呢?我们可以通过以下的例子得出结论:

输出

以上例子中,我们从对象a中解构赋值了name、age、marriage、addr四个变量,分别是 string 、 number 、 boolean 、 object 类型。改变这四个变量的值后,再与a原来的值作对比,我们发现a的name,age,marriage属性没有改变,而addr属性发生了改变。由此可以得出结论,解构赋值对object类型只是浅拷贝。

实际上,无论是使用扩展运算符(...)还是解构赋值,对于引用类型都是浅拷贝。所以在使用spl()、concat()、...对数组拷贝时,只有当数组内部属性值不是引用类型是,才能实现深拷贝。

Object.assign()是深拷贝还是浅拷贝?

定义:Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象到目标对象。它将返回目标对象。

Object.assign()拷贝的是 属性值 。如源对象的属性值是一个对象的引用,那么它也只指向那个引用。也就是说,如果对象的属性值为 简单类型 (如string, number),通过Object.assign({},srcObj);得到的新对象为深拷贝;如果属性值为 对象或其它引用类型 ,那对于这个对象而言其实是浅拷贝的。

1.用 JSON.stringify 把对象转换成字符串,再用 JSON.parse 把字符串转换成新的对象,但是需要注意的是,可以转成 JSON 格式的对象才能使用这种方法,如果对象中包含 function 或 RegExp 这些就不能用这种方法了。

2.Object.assign()拷贝

当对象中只有一级属性,没有二级属性的时候,此方法为深拷贝,但是对象中有对象的时候,此方法,在二级属性以后就是浅拷贝。

3.使用递归的方式实现深拷贝

此外,通过jQuery的extend方法实现深拷贝,lodash.cloneDeep()实现深拷贝。

参考文档: object.assign()-MDN

---------------------------------------------------------------------------------

以上为个人理解和总结,欢迎讨论。

JS面试题:深浅拷贝简析/手写深拷贝函数

对象类型在赋值的过程中实际上是了地址,从而导致了其中一方被改变其他也都被改变的情况,在开发中我们通常不希望出现这样的问题,这里可以使用浅拷贝来解决这个情况。

首先我们可以通过Object.assign来实现浅拷贝,该函数只会拷贝所有的属性值到新的对象中,如果属性值是对象的话,拷贝的是地址,即为浅拷贝而不是深拷贝。

以下为Object.assign浅拷贝的简单实现:

还可以通过展开运算符...来实现浅拷贝:

但是,浅拷贝只解决了层的问题,如果对象下还有对象的话,那么又回到最开始的问题了,第二层的对象拷贝过来的只是地址,两者享有相同的地址,这时就需要用到深拷贝了。

我们通常使用JSON.parse(JSON.stringify(object))来解决:

但是该方法具有以下局限性:

遇到函数、undefined和symbol时,会直接忽略掉他们,该对象不能正常的序列化,此时我们需要实现一个更为完善的深拷贝。

小结:以上深拷贝的方法依然只是较为简易的,要想实现一个比较完美的深拷贝其实是很困难的,需要我们考虑很多种边界情况,比如原型链如何处理、DOM如何处理等。该deepClone函数就有两个较为明显的问题,一是没有解决对象的循环引用的问题(参考方案:用弱映射做一个哈希表,存储原对象,若缓存命中,则过滤本次拷贝,直接使用记忆化数据,否则惰性拷贝。一般不是为了解决IE的兼容性问题,都没有问题,考虑兼容性则按需垫片。生产环境其实还需要考虑其它类型的拷贝,一般直接使用辅助工具库。总而言之,按需拷贝);二是无法实现函数的拷贝。

js Object.assign()函数以及深、浅拷贝

object.assign():用于将源对象(source)中可枚举的属性到目标属性(target)中,并返回目标对象。

let a={a:1};let b={b:2};let c={c:3};

console.log(Object.assign(a,b,c));

//{ a: 1, b: 2, c: 3 }

浅拷贝:也叫引用拷贝,公用一块内存地址,一个改变另一个也改变;

深拷贝:创建新的内存地址保存值,与原对象完全。

深拷贝和浅拷贝只针对复杂的复杂的对象有别;

注意:针对object.assign()而言,如果属性值是简单类型(number,string),通过object.assign({},src)得到的新对象是深拷贝对象;如果属性值为对象或其他引用类型,得到的新对象为浅拷贝对象。

深拷贝与浅拷贝的实现(一)

最近的学习中,仔细研究了下深拷贝和浅拷贝,下面就来简单的总结下。

首先我们了解下两种 数据类型 :

1、基本类型:像Number、String、Boolean等这种为基本类型

2、复杂类型:Object和Array

接着我们分别来了解下浅拷贝和深拷贝,深拷贝和浅拷贝是只针对Object和Array这样的复杂类型的。

浅拷贝 :

可以看出,对于对象或数组类型,当我们将a赋值给b,然后更改b中的属性,a也会随着变化。也就是说a和b指向了同一块内存,所以修改其中任意的值,另一个值都会随之变化,这就是浅拷贝。

深拷贝 :

刚刚我们了解了什么是浅拷贝,那么相应的,如果给b放到新的内存中,将a的各个属性都到新内存里,就是深拷贝。

也就是说,当b中的属性有变化的时候,a内的属性不会发生变化。

那么除了上面简单的赋值引用,还有哪些方法使用了 浅拷贝 呢?

Object.assign()

在MDN上介绍Object.assign():”Object.assign() 方法用于将所有可枚举的属性的值从一个或多个源对象到目标对象。它将返回目标对象。”

一个对象

可以看到,Object.assign()拷贝的只是属性值,如源对象的属性值是一个指向对象的引用,它也只拷贝那个引用值。所以Object.assign()只能用于浅拷贝或是合并对象。这是Object.assign()值得注意的地方。

那么下面我们就来说说复杂的 深拷贝 。

jQuery.extend()

说到深拷贝,想到的就是jQuery.extend()方法,下面我们简单看下jQuery.extend()的使用。

jQuery.extend( [deep ], target, object1 [, objectN ] ),其中deep为Boolean类型,如果是true,则进行深拷贝。

我们还是用上面的数据来看下extend()方法。

通过上面的对比可以看出,当使用extend()进行深拷贝的时候,对象的所有属性都添加到target中了。

我们知道了extend()可以进行深拷贝,那么extend()是如何实现深拷贝的呢?

先来看下jQuery.extend()源码

主要看下关于深拷贝的部分,取个参数,如果是boolean类型的,就赋值给deep,下面如果deep为true(也就是进行深拷贝),就递归调用extend(),这样就将对象的所有属性都添加到了target中实现了深拷贝。

JSON.parse()和JSON.stringify()

上面的jQuery源码是否让你眼花缭乱?有没有什么办法无脑实现深拷贝呢?JSON.parse()和JSON.stringify()给了我们一个基本的解决办法。

可以看到改变targetCopy并没有改变原始的target,继承的属性也没有丢失,因此实现了基本的深拷贝。

但是用JSON.parse()和JSON.stringify()会有一个问题。

JSON.parse()和JSON.stringify()能正确处理的对象只有Number、String、Array等能够被json表示的数据结构,因此函数这种不能被json表示的类型将不能被正确处理。

上面的例子可以看出,hello这个属性由于是函数类型,使用JSON.parse()和JSON.stringify()后丢失了。

因此JSON.parse()和JSON.stringify()还是需要谨慎使用。