阅读 223

JavaScript中 浅拷贝与深拷贝的理解及实现

1. 相关知识点

1.1  基本类型与引用类型分类

基本类型:number,string,boolean,null,undefined,symbol及BigInt(任意精度整数)七类; 引用类型:对象、数组、函数等;

1.2  JS中变量的存储方式

栈:自动分配内存空间,系统自动释放,里面存放的是基本类型的名值和引用类型的名(地址)

堆:动态分配的内存,大小不定,也不会自动释放,里面存放引用类型的值

1.2.1 基本类型

let a = 1;

在这里插入图片描述

当你b = a复制时,栈内存会新开辟一个内存

在这里插入图片描述

当你此时修改a=2,对b并不会造成影响;

let a=1,b=a;

虽然b不受a影响,但这也算不上深拷贝。因为深拷贝本身只针对较为复杂的object类型数据。

1.2.2 引用数据类型

let a = [0,1,2,3,4];

在这里插入图片描述

当 b = a 进行拷贝时,其实复制的是a的引用地址,而并非堆里面的值;

在这里插入图片描述

当a[0] = 1时进行数组修改时,由于a与b指向的是同一个地址,所以自然b也受了影响, 这就是浅拷贝。

在这里插入图片描述

要是在堆内存中也开辟一个新的内存专门为b存放值(就像基本类型那样),就达到深拷贝的效果了。

在这里插入图片描述

2. 实现浅拷贝的方法

2.1 for···in 只循环第一层

// 只复制第一层的浅拷贝 function simpleCopy(obj1) {   var obj2 = Array.isArray(obj1) ? [] : {};   for (let i in obj1) {    obj2[i] = obj1[i];   }   return obj2; } var obj1 = {   a: 1,   b: 2,   c: {     c1: 3   } } var obj2 = simpleCopy(obj1); obj2.a = 3; obj2.c.c1 = 4; console.log(obj1.a,obj2.a); // 1  3 console.log(obj1.c.c1,obj2.c.c1); // 4  4 复制代码

2.2 Object.assign方法

var obj1 = {   a: 1,   b: 2,   c: {     c1: 3   } } var obj2 = Object.assign({},obj1); obj2.a = 3; obj2.c.c1 = 4 console.log(obj1.a,obj2.a) // 1  3 console.log(obj1.c.c1,obj2.c.c1); // 4  4 复制代码

2.3 直接用 = 赋值

let a = [0,1,2,3,4],     b = a; console.log(a===b); // true a[0]=1; console.log(a,b); // [1,1,2,3,4]   [1,1,2,3,4] 复制代码

3. 实现深拷贝的方法

3.1 采用递归去拷贝所有层级属性

function deepClone(obj){   let objClone = Array.isArray(obj)?[]:{};   if(obj && typeof obj === "object"){     for(key in obj){       if(obj.hasOwnProperty(key)){           // 判断ojb子元素是否为对象,如果是,递归复制         if(obj[key] && typeof obj[key] === "object"){           objClone[key] = deepClone(obj[key]);         }else{           // 如果不是,简单复制           objClone[key] = obj[key];         }       }     }   }   return objClone; }     let a = [1,2,3,4] let b = deepClone(a); a[0]=2; console.log(a,b); // [2,2,3,4]  [1,2,3,4] let obj1 = {   a: 1,   b: 2,   c: {     c1: 3   } } let obj2 = deepClone(obj1) obj2.c.c1 = 4 console.log(obj1,obj2); // {a:1,b:2,c:{c1:3}}   {a:1,b:2,c:{c1:4}} 复制代码

3.2 通过JSON对象来实现深拷贝

注意: 无法实现对象中方法的深拷贝

function deepClone(obj) {   var _obj = JSON.stringify(obj),     objClone = JSON.parse(_obj);   return objClone; }   let a = [1,2,3,4] let b = deepClone(a); a[0]=2; console.log(a,b); // [2,2,3,4]  [1,2,3,4] let obj1 = {   a: 1,   b: 2,   c: {     c1: 3,     c2:function(){ // 无法实现对对象中方法的深拷贝       console.log('深拷贝')      }   } } let obj2 = deepClone(obj1) obj2.c.c1 = 4 console.log(obj1,obj2); // {a:1,b:2,c:{c1:3,c2:f()}}   {a:1,b:2,c:{c1:4}} 复制代码

3.3 通过 jQuery 中的 extend 方法实现深拷贝

$.extend( [deep ], target, object1 [, objectN ] )

deep表示是否深拷贝,true为深拷贝,false为浅拷贝

target Object类型 目标对象,其他对象的成员属性将被附加到该对象上;

object1  objectN(可选), Object类型 第一个以及第N个被合并的对象;

var array1 = [1,2,3,4]; var newArray = $.extend(true,[],array1); // true为深拷贝,false为浅拷贝 复制代码

3.4 lodash函数库 实现深拷贝

lodash中文网

let result = _.cloneDeep(test) 复制代码

3.5 Reflect法

reflect简介

function isObject(val) {   return val != null && typeof val === 'object' && Array.isArray(val) === false; } function deepClone(obj) {   let isArray = Array.isArray(obj)   let cloneObj = isArray ? [...obj] : { ...obj }   Reflect.ownKeys(cloneObj).forEach(key => {     cloneObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]   })   return cloneObj } let a = [1,2,3,4] let b = deepClone(a); a[0]=2; console.log(a,b); // [2,2,3,4]  [1,2,3,4] let obj1 = {   a: 1,   b: 2,   c: {     c1: 3,     c2:function(){       console.log('深拷贝')     }   } } let obj2 = deepClone(obj1) obj2.c.c1 = 4 console.log(obj1,obj2); // {a:1,b:2,c:{c1:3,c2:f}}   {a:1,b:2,c:{c1:4,c2:f}} 复制代码

3.6 手动实现深拷贝

let obj1 = {    a: 1,    b: 2 } let obj2 = {    a: obj1.a,    b: obj1.b } obj2.a = 3; alert(obj1.a); // 1 alert(obj2.a); // 3 复制代码

3.7  Object.assign 方法

注意:

如果对象的 value 是基本类型,可用 Object.assign 来实现深拷贝

// 对象的 value 是基本类型 var obj = {   a: 1,   b: 2, } var obj1 = Object.assign({}, obj); // obj赋值给一个空{} obj1.a = 3; console.log(obj.a) // 1 复制代码

3.8  slice 实现对数组的深拷贝

注意:

当数组里面的值是基本数据类型,比如String,Number,Boolean时,属于深拷贝;

当数组里面的值是引用数据类型,比如Object,Array时,属于浅拷贝;

// 当数组里面的值是基本数据类型,比如 String,Number,Boolean 时,属于深拷贝 // 当数组里面的值是引用数据类型,比如 Object,Array 时,属于浅拷贝 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"] 复制代码

3.9 concat 实现对数组的深拷贝

注意:

当数组里面的值是基本数据类型,比如 String,Number,Boolean 时,属于深拷贝;

当数组里面的值是引用数据类型,比如 Object,Array 时,属于浅拷贝;

// 当数组里面的值是基本数据类型,比如String,Number,Boolean时,属于深拷贝; // 当数组里面的值是引用数据类型,比如Object,Array时,属于浅拷贝; 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 = [{a:1},{b:2},{c:3}]; var arr2 = arr1.concat(); arr2[0].a = "9"; console.log(arr1[0].a); // 9 console.log(arr2[0].a); // 9 复制代码

3.10   通过 var newObj = Object.create(oldObj)

注意: 无法实现对对象中方法的深拷贝;

function deepClone(initalObj, finalObj) {       var obj = finalObj || {};       for (var i in initalObj) {             var prop = initalObj[i];  // 避免相互引用对象导致死循环,如 initalObj.a = initalObj 的情况     if(prop === obj) {                   continue;     }             if (typeof prop === 'object') {       obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);     } else {       obj[i] = prop;     }   }       return obj; } let obj1 = {   a: 1,   b: 2,   c: {     c1: 3,     c2:function(){       console.log('深拷贝')     }   } } let obj2 = deepClone(obj1) obj2.c.c1 = 4 console.log(obj1,obj2); // {a:1,b:2,c:{c1:3,c2:f}}   {a:1,b:2,c:{c1:4}} 复制代码

3.11 使用 扩展运算符 实现深拷贝

注意:

当value是基本数据类型,比如 String,Number,Boolean 时,属于深拷贝;

当value是引用类型的值,比如 Object,Array,属于浅拷贝;

let obj1 = {   a: 1,   b: 2,   c: {     c1: 3,     c2:function(){       console.log('深拷贝')     }   } } let obj2 = {...obj1}  // 此时拷贝了 {c1:3} 的引用地址 obj2.c.c1 = 4        // 改变对象里面的值 console.log(obj1,obj2);  // {a:1,b:2,c:{c1:4,c2:f}}   {a:1,b:2,c:{c1:4,c2:f}} let obj3 = {...obj1}   obj3.c = {c1:5}     // 改变引用的对象,实际改变了引用对象的地址 console.log(obj1,obj3);   // {a:1,b:2,c:{c1:4,c2:f}}   {a:1,b:2,c:{c1:5}}


作者:明天也要努力
链接:https://juejin.cn/post/7031538876535537695


文章分类
代码人生
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐