本文参考: Object.assign 原理及其实现 需求场景 上一篇文章:手写实现深拷贝中,我们讲了浅拷贝和深拷贝,也实现了深拷贝方案。 上一篇的深拷贝方案虽然可以实现深度拷贝,但却不支持拷贝到一个目标对象上,而 Object.assign 虽然支持拷贝到目标对象上,但它只是浅拷贝,只处理第一层属性的拷贝。 但两种方案结合一下,其实也就是该需求的实现方案了,所以要么扩展深拷贝方案,增加与目标对象属性的交集处理和冲突处理;要么扩展 Object.assign,让它支持深拷贝。 实现方案 本篇就选择基于 Object.assign,扩展支持深拷贝:assignDeep。 这里同样会给出几个方案,因为深拷贝的实现可以用递归,也可以用循环,递归比较好写、易懂,但有栈溢出问题;循环比较难写,但没有栈溢出问题。
实现浅拷贝与深拷贝 Js包含基本数据类型与引用数据类型两种不同的数据类型的值,深拷贝与浅拷贝的概念只存在于引用数据类型。 原生方法实现 原生方法实现浅拷贝,可以使用{...obj}与Object.assign({}, obj)等方式,{...obj}主要是使用了Spread操作符将对象表达式展开构造字面量对象的方式实现浅拷贝 关于Object.assign是浅拷贝还是对于第一层是深拷贝之后是浅拷贝的说法,主要取决于如何理解浅拷贝与深拷贝的概念,假如同本文一样认为只有引用类型才有浅拷贝与深拷贝的概念的话,那么Object.assign 就是浅拷贝;假如认为对于基本数据类型也有浅拷贝与深拷贝的概念的话,那么如上文所述对于基本数据类型的拷贝可以理解为按值深拷贝,那么关于Object.assign第一层是深拷贝,第二层及以后是浅拷贝的说法也是没有问题的 原生方法实现深拷贝,主要是使用JSON.parse()与JSON.stringify(),首先将对象序列化为JSON字符串,再将JSON字符串反序列化为对象,使用这种方式效率比较高,但是会有一些问题,对于循环引用的对象无法实现深拷贝
代金券、腾讯视频VIP、QQ音乐VIP、QB、公仔等奖励等你来拿!
最常用的深拷贝方法 1.Object.assign 此方法是es6新推出来的方法,目的是将所有可枚举属性的值从一个或多个源对象分配到目标对象 Object.assign(目标对象, 源对象)该方法参数可以有一个 注意:assign 的属性拷贝是浅拷贝(踩过的深坑) 2.无意中看到一种方法,效果与assign一样,只能实现一级拷贝 let obj1 = {name: 'dandan', love: {car 虽然这种方法可以成功实现嵌套属性的深拷贝,但是也有许多弊端。 如果对象中存在循环引用的情况也无法正确实现深拷贝; 4.自定义对象数组深拷贝 function deepClone(obj) { if (typeof obj ! == "object") { return "克隆的值不是对象哦"; } //判断拷贝的要进行深拷贝的是数组还是对象,是数组的话进行数组拷贝,对象的话进行对象拷贝 var objClone
【前端芝士树】浅拷贝、深拷贝以及Object.assign()的作用 首先还是得回到Javascript的基本数据类型。 (a) // 输出 {b: 2} 所以深拷贝问题的出现就是为了解决引用类型的数据的浅拷贝特性 实现对象深拷贝的几种方法 JSON.parse() && JSON.stringfy() 将该对象转换为其 Array.slice() 和 Array.concat() 这两个方法,仅适用于对不包含引用对象的一维数组的深拷贝! Object.assign() 方法 以及 对象扩展操作符 ... Object.assign() 方法 Object.assign()考察点是ES6中实现对象复制,关于Object.assign()这个函数这里有一篇文章讲得非常详细明白。 : 2, c: 3 } 那么Object.assign()方法是浅拷贝还是深拷贝呢?
浅拷贝: 思路----------把父对象的属性,全部拷贝给子对象,实现继承。 问题---------如果父对象的属性等于数组或另一个对象,那么实际上,子对象获得的只是一个内存地址,不会开辟新栈,不是真正拷贝,因此存在父对象被篡改的可能。 : function deepCopy(o){ var f = {}; for(i in o){ f[i] = o[i]; }; return f } 深拷贝 : 思路-----------递归调用'浅拷贝',可以解决子对象修改时会污染父对象,此时两个对象指向的不是一个内存地址。
Object.assign({},obj1,obj2,obj3); 深浅拷贝 既然说到了拷贝,我们应该考虑的是这个方法实现的是深拷贝还是浅拷贝。还是以实例来看结果比较直观。 我们可以发现这是典型的浅拷贝,针对深拷贝,需要使用其他办法,在此不过多介绍,详细可以参阅js的深拷贝和浅拷贝。所以假如源对象的属性值是一个对象的引用,那么复制拷贝结果也只指向那个引用。 很明显,继承属性和不可枚举属性是不能拷贝的,这一原理其实和JSON.stringify是相同的,返回的都是自身可枚举属性。 从上面的一系列例子中我们也可以看出,Object.assign执行是有顺序的,从左往右依次执行复制操作,但是如果中间复制出现异常,则后续的复制操作则会被打断。 String类型和 Symbol 类型的属性都会被拷贝。 在出现错误的情况下,例如,如果属性不可写,会引发TypeError,如果在引发错误之前添加了任何属性,则可以更改target对象。
我也有些疑惑,于是我去MDN搜一下拷贝相关内容,发现并没有关于拷贝的实质概念,没有办法只能通过实践了,同时去看一些前辈们的文章总结了这篇关于拷贝的内容,本文也属于公众号【程序员成长指北】学习路线中【JS 自己实现一个浅拷贝 实现原理:新的对象复制已有对象中非对象属性的值和对象属性的引用,也就是说对象属性并不复制到内存。 JSON.stringify()虽然可以实现深拷贝,但是还有一些弊端比如不能处理函数等。 JSON.stringify()实现深拷贝注意点 拷贝的对象的值中如果有函数,undefined,symbol则经过JSON.stringify()序列化后的JSON字符串中这个键值对会消失 无法拷贝不可枚举的属性 (即obj[key] = obj) 自己实现一个简单深拷贝 深拷贝,主要用到的思想是递归,遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝。
但是 Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。因为 Object.assign()拷贝的是属性值。 假如源对象的属性值是一个指向对象的引用,它也只拷贝那个引用值。Object.assgin 只能深拷贝第一层, 深层的还是浅拷贝, 记住这个就行了。 是浅拷贝,拷贝的是对象的引用值,如果为引用类型对象时,一级属性为深拷贝,对象中有二级属性的话,则二级属性以后都是浅拷贝。 20 } } console.log(obj1 === obj2); // false console.log(obj1.body === obj2.body); // false 这种方法虽然可以实现数组或对象深拷贝 JSON.stringify(test)); test.name = "test"; console.error("ddd", test, copyed); 6.如果对象中存在循环引用的情况也无法正确实现深拷贝
深拷贝 基本类型:拷贝值 引用类型:会创建一个新的引用,将之前的对象完整的拷贝一份出来,并添加至新的引用当中。 #浅拷贝 #Object.assign() 使用原生的 Object.assign() 方法就可以实现引用类型的浅拷贝 let obj1 = { value: 'a' } let obj2 = 这个可以参考loadsh cloneDeep #自己实现深拷贝 function deepClone(obj, hash = new WeakMap()) { if (obj === null) return 指向的是当前类本身 hash.set(obj, cloneObj); for (let key in obj) { if (obj.hasOwnProperty(key)) { // 实现一个递归拷贝 ES6-扩展运算符 juejin-浅拷贝与深拷贝 如何写出一个惊艳面试官的深拷贝?
Object.assign(undefined) // 报错 Object.assign(null) // 报错 如果非对象参数出现在源对象的位置(即非首参数),那么处理规则有所不同。 只有字符串的包装对象,会产生可枚举的实义属性,那些属性则会被拷贝。 Object.assign拷贝的属性是有限制的,只拷贝源对象的自身属性(不拷贝继承属性),也不拷贝不可枚举的属性(enumerable: false)。 方法实行的是浅拷贝,而不是深拷贝。 一些函数库提供 Object.assign的定制版本(比如 Lodash 的_.defaultsDeep方法),可以得到深拷贝的合并。
Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target),类似这样: const target = { a: 1 }; const source1 c:3} 那是不是Object.assign({},obj)就可以实现深拷贝了? ==a 说明拷贝成功!但是,如果对象中再包含对象,结果会怎样 ? 我们从打印的内容可以看出,a中的成员a和copy中的成员a是完全相等的,意味着在栈中的地址指向是相同的,所以a中的成员a并没有实现深拷贝,这个时候你更改a.a,copy.a也会跟着变化。 ? 结论:【Object.assign方法无法实现深复制】它只是比浅拷贝深了一层而已。
浅拷贝的实现 浅拷贝的实现方法比较简单,只要使用是简单的复制语句即可。 1.1 方法一:简单的复制语句 ? ? 深拷贝的实现 要实现深拷贝有很多办法,有最简单的 JSON.parse() 方法,也有常用的递归拷贝方法,和ES5中的 Object.create() 方法。 2.1 方法一:使用 JSON.parse() 方法 要实现深拷贝有很多办法,比如最简单的办法是使用 JSON.parse(): ? ? 这种方法简单易用。 RegExp对象是无法通过这种方式深拷贝。 2.2 方法二:递归拷贝 代码如下: ? 上述代码确实可以实现深拷贝。但是当遇到两个互相引用的对象,会出现死循环的情况。 参考:jQuery.extend()方法的实现 jQuery.js的jQuery.extend()也实现了对象的深拷贝。下面将官方代码贴出来,以供参考。
但是,除了字符串会以数组形式,拷贝入目标对象,其他值都不会产生效果。 Object.assign只拷贝自身属性,不可枚举的属性(enumerable为false)和继承的属性不会被拷贝。 有一些函数库提供Object.assign的定制版本(比如Lodash的_.defaultsDeep方法),可以解决深拷贝的问题。 Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。 ,源对象obj1的a属性的值是一个对象,Object.assign拷贝得到的是这个对象的引用。 注: 由于存在深拷贝的问题,DEFAULTS对象和options对象的所有属性的值,都只能是简单类型,而不能指向另一个对象。否则,将导致DEFAULTS对象的该属性不起作用。 三、浏览器支持 ? ?
也就是实现的浅拷贝的功能差不多,但是如果属性都是基本类型的值,使用扩展运算符进行浅拷贝会更加方便。 如果存在对象的嵌套,那么浅拷贝将无能为力。因此深拷贝就是为了解决这个问题而生的,它能解决多层对象嵌套问题,彻底实现拷贝。 深拷贝则不同,对于复杂引用数据类型,其在堆内存中完全开辟了一块内存地址,并将原有的对象完全复制过来存放。 深拷贝后的对象与原始对象是相互独立、不受影响的,彻底实现了内存上的分离。 总的来说,深拷贝的原理可以总结如下: 将原对象从内存中完整地拷贝出来一份给新对象,并从堆内存中开辟一个全新的空间存放新对象,且新对象的修改并不会改变原对象,二者实现真正的分离。 1. 实现深拷贝方法 下面是一个实现 deepClone 函数封装的例子,有几点需要注意下。
但是 Object.assign和对象的扩展运算对只有一层的对象进行的是深拷贝。 Python 中的深拷贝 在 Python 中实现复杂对象的拷贝可以通过标准库copy 提供的 copy.deepcopy 实现,此外 copy 模块还提供了 copy.copy 进行对象的浅拷贝。 手动实现深拷贝操作 在某些情况下需要我们实现深拷贝操作,比如对自定义数据类型进行深拷贝。 前面 JS 所述使用 JSON 进行的深拷贝方法仍有缺陷,比如:会忽略 undefined、会忽略 symbol、不能序列化函数、不能解决循环引用的对象。这时候就需要了解波深拷贝的实现了。 So,以 deepcopy 层次 Object 为例子,要实现真正的深拷贝操作则需要通过遍历键来赋值对应的值,这个过程中如果遇到 Object 类型还需要再次进行遍历「同样的方法」。递归无疑了。
云端获取和启用云服务器,并实时扩展或缩减云计算资源。云服务器 支持按实际使用的资源计费,可以为您节约计算成本。 腾讯云服务器(CVM)为您提供安全可靠的弹性云计算服务。只需几分钟,您就可以在云端获取和启用云服务器,并实时扩展或缩减云计算资源。云服务器 支持按实际使用的资源计费,可以为您节约计算成本。
扫码关注云+社区
领取腾讯云代金券