js深拷贝与浅拷贝的区别(JavaScript 深拷贝和浅拷贝)
一 、前言
hello ,大家好~ ,本文主要介绍在 JavaScript 中什么是深拷贝和浅拷贝 ,以及如何实现一个对象的深拷贝 。
二 、随处可见的 “赋值 ”
在 JavaScript 中我们最常见的操作之一是将一个变量的值赋值给另一个变量 ,这个过程我们也可以称为 “拷贝 ” 一份变量的值给另一个变量 。
2.1 基本数据类型的赋值操作
在涉及到基本数据类型(string 、Boolean 、number...)赋值操作的时候 ,原始变量值被复制给另外一个变量 。我们来考虑下下面这段代码:我们将值为 10 的变量 x 赋值给变量 y ,此时 x 和 y 已经失去“联系 ”了 ,所以改变 x 的值不会影响 y 的值 ,最后输出 20 和 10 。
<script> let x = 10; let y = x; x += 10; console.log(x, y); // 20 10 </script>2.2 引用数据类型的赋值操作
与基本数据类型赋值相反 ,引用数据类型(Object 、Array...)在赋值的时候传递的只是一个 引用 (原始值和拷贝赋值后的值指向同一块内存空间) ,所以当被复制出来的对象值改变的时候 ,原始的对象值也会跟着改变 ,这种现象被称为 浅拷贝 。
对象赋值案例: 将 user 对象赋值给 user2 变量,修改 user2 属性值后 user 属性值也会跟着变 ,两者的值始终保持同步 ,这便是浅拷贝带来的 副作用 ,拷贝出来的值和原始值始终保持着关联 。基本数据类型的赋值没有这种现象 ,所以说 浅拷贝 和 深拷贝 是相对引用数据类型而言的 。
<script> let user = { name: zjl712, age: 18, local: shanghai } let user2 = user; user2.name = zjl; console.log(user:, user, user2:, user2); </script>三 、实现对象的深拷贝
通过以上的介绍我们知道什么是 浅拷贝 以及浅拷贝带来的 副作用 ,为了消除这种“副作用 ”我们需要对对象进行 深拷贝 。下面将介绍一些对象拷贝的方法 ,它们各有优缺点 。
"=" 号直接赋值通过等号(=)将原始值赋值给一个新的值 ,优点是简便快捷 ,缺点 是只能进行 浅拷贝
let obj = {name:zjl712, age:18} let obj2 = obj; console.log(obj == obj2) //true Object.assign()通过 Object 的 assign 方法可以对对象进行深拷贝 ,缺点是不能对含有多层嵌套结构的对象进行深拷贝。
深拷贝单层结构的对象 ,改变拷贝对象的值不会改变原始对象的值 。
<script> let obj = { name: zjl712, age: 18, getName:function(){ return this.name } } let obj2 = Object.assign({}, obj) console.log(obj == obj2); // false obj2.name = zjl console.log(obj, obj2); </script>尝试拷贝多层嵌套结构对象 ,改变拷贝对象内嵌套的对象值(obj2.local.nickname)后,原始对象嵌套的值也会跟着改变 。改变拷贝对象非嵌套的值 ,原始对象的值不会跟着变。所以 Object.assign() 只能实现部分深拷贝 。
<script> let obj = { name: zjl712, age: 18, getName:function(){ return this.name }, local: { name: shanghai, nickname: modu, } } let obj2 = Object.assign({}, obj) console.log(obj == obj2); // false obj2.local.nickname = 魔都 obj2.name = zjl console.log(obj, obj2); console.log(obj.local == obj2.local) // true </script> JSON.stringify() 和 JSON.parse()JSON.stringify() 方法接收一个对象作为参数,将对象转为 JSON 字符串 。JSON.parse() 接收一个 JSON 字符串 ,将该字符串转为一个对象。结合这两个方法可以对一个对象进行深拷贝 。
结合以上两个方法对对象进行深拷贝
let obj = {name: zjl712,age: 18} let obj2 = JSON.parse(JSON.stringify(obj)) console.log(obj == obj2); // false obj2.name = zjl console.log(obj, obj2);当对象属性值含有 函数(function) 时,且含有嵌套对象时 。拷贝的值 属性值为函数的属性会丢失 。优点是能对多层嵌套对象进行深拷贝 。
let obj = { name: zjl712, age: 18, getName:function(){ return this.name }, local: { name: shanghai, nickname: modu, } } let obj2 = JSON.parse(JSON.stringify(obj)) console.log(obj == obj2); // false obj2.local.nickname = 魔都 obj2.name = zjl console.log(obj, obj2); console.log(obj.local == obj2.local) // false 对象扩展符(...)使用 ES6 的扩展操作符 ,使用三个点(...)将原始对象的值拷贝给另一个对象 。然而三点扩展符和 Object.assign() 很相似,扩展符不能对多层嵌套对象进行深拷贝 。
let obj = { name: zjl712, age: 18, getName:function(){ return this.name }, local: { name: shanghai, nickname: modu, } } let obj2 = {...obj} console.log(obj == obj2); // false obj2.local.nickname = 魔都 obj2.name = zjl console.log(obj, obj2); 递归的方式实现对象的深拷贝实现含有嵌套结构 、函数对象的深拷贝以上方法都无能为力 ,我们可以用递归的方法完成对象的深拷贝 。缺点是对复杂的对象(Buffer 、Date 等实例对象)无法实现深拷贝
function myDeepClone(obj){ let clone; // 排除非引用类型数据 if(obj == null || typeof obj != object) return obj; if(Array.isArray(obj)){ // obj 是数组 clone = new obj.constructor(obj.length) obj.forEach((value, index) => { clone[index] = typeof value === object?myDeepClone(value):value }) }else{ // 浅拷贝一份原始数据 clone = Object.assign({}, obj) // 递归 clone 内的每一个属性值 Object.keys(clone).forEach(key => { clone[key] = typeof obj[key] === object?myDeepClone(obj[key]):obj[key] }) } return clone; }以上递归代码有相似代码(两个 forEach 内的代码) ,可简化为以下代码:
function myDeepClone(obj){ let clone; if(obj == null || typeof obj != object) return obj; clone = Object.assign({}, obj) Object.keys(clone).forEach(key => { clone[key] = typeof obj[key] === object?myDeepClone(obj[key]):obj[key] }) if(Array.isArray(obj)){ clone.length = obj.length clone = Array.from(clone) } return clone } 以上的自定义递归方法不够全面,但对于基本的对象( {} )和数组( [] )深拷贝还是够用的 ,要想实现复杂对象的深拷贝可以使用 lodash 的 cloneDeep 方法 ,这个方法实现深拷贝是比较完美的 。 // 安装 lodash :npm i lodash // 使用 let lang = require(lodash/lang) let clone = lang.cloneDeep(obj)创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!