南阳做网站公司哪家好,织梦做小游戏网站,成品短视频代码推荐大全,嘉兴seo报价工作中经常会遇到需要复制 JavaScript 数据的时候#xff0c;遇到 bug 时实在令人头疼#xff1b;面试中也经常会被问到如何实现一个数据的深浅拷贝#xff0c;但是你对其中的原理清晰吗#xff1f;一起来看一下吧#xff01;一、为什么会有深浅拷贝想要更加透彻的理解为什…工作中经常会遇到需要复制 JavaScript 数据的时候遇到 bug 时实在令人头疼面试中也经常会被问到如何实现一个数据的深浅拷贝但是你对其中的原理清晰吗一起来看一下吧一、为什么会有深浅拷贝想要更加透彻的理解为什么 JavaScript 会有深浅拷贝需要先了解下 JavaScript 的数据类型有哪些一般分为基本类型(Number、String、Null、Undefined、Boolean、Symbol )和引用类型(对象、数组、函数)。基本类型是不可变的任何方法都无法改变一个基本类型的值也不可以给基本类型添加属性或者方法。但是可以为引用类型添加属性和方法也可以删除其属性和方法。基本类型和引用类型在内存中的存储方式也大不相同基本类型保存在栈内存中而引用类型保存在堆内存中。为什么要分两种保存方式呢 因为保存在栈内存的必须是大小固定的数据引用类型的大小不固定只能保存在堆内存中但是我们可以把它的地址写在栈内存中以供我们访问。说来这么多我们来看个示例let num1 10;let obj1 {name: hh}let num2 num1;let obj2 obj1;num2 20;obj2.name kk;console.log(num1); // 10console.log(obj1.name); // kk执行完这段代码内存空间里是这样的可以看到 obj1 和 obj2 都保存了一个指向该对象的指针所有的操作都是对该引用的操作所以对 obj2 的修改会影响 obj1。小结之所以会出现深浅拷贝是由于 JS 对基本类型和引用类型的处理不同。基本类型指的是简单的数据段而引用类型指的是一个对象保存在堆内存中的地址JS 不允许我们直接操作内存中的地址也就是说不能操作对象的内存空间所以我们对对象的操作都只是在操作它的引用而已。在复制时也是一样如果我们复制一个基本类型的值时会创建一个新值并把它保存在新的变量的位置上。而如果我们复制一个引用类型时同样会把变量中的值复制一份放到新的变量空间里但此时复制的东西并不是对象本身而是指向该对象的指针。所以我们复制引用类型后两个变量其实指向同一个对象所以改变其中的一个对象会影响到另外一个。二、深浅拷贝1. 浅拷贝浅拷贝只是复制基本类型的数据或者指向某个对象的指针而不是复制对象本身源对象和目标对象共享同一块内存若对目标对象进行修改存在源对象被篡改的可能。我们来看下浅拷贝的实现/* sourceObj 表示源对象* 执行完函数返回目标对象*/function shadowClone (sourceObj {}) {let targetObj Array.isArray(sourceObj) ? [] : {};let copy;for (var key in sourceObj) {copy sourceObj[key];targetObj[key] copy;}return targetObj;}// 定义 sourcelet sourceObj {number: 1,string: source1,boolean: true,null: null,undefined: undefined,arr: [{name: arr1}, 1],func: () sourceFunc1,obj: {string: obj1,func: () objFunc1}}// 拷贝sourceObjlet copyObj shadowClone(sourceObj);// 修改 sourceObjcopyObj.number 2;copyObj.string source2;copyObj.boolean false;copyObj.arr[0].name arr2;copyObj.func () sourceFunc2;copyObj.obj.string obj2;copyObj.obj.func () objFunc2;// 执行console.log(sourceObj);/* {number: 1,string: source1,boolean: true,null: null,undefined: undefined,arr: [{name: arr2}],func: () sourceFunc1,obj: {func: () objFunc2,string: obj2}}*/2. 深拷贝深拷贝能够实现真正意义上的对象的拷贝实现方法就是递归调用“浅拷贝”。深拷贝会创造一个一模一样的对象其内容地址是自助分配的拷贝结束之后内存中的值是完全相同的但是内存地址是不一样的目标对象跟源对象不共享内存修改任何一方的值不会对另外一方造成影响。/* sourceObj 表示源对象* 执行完函数返回目标对象*/function deepClone (sourceObj {}) {let targetObj Array.isArray(sourceObj) ? [] : {};let copy;for (var key in sourceObj) {copy sourceObj[key];if (typeof(copy) object) {if (copy instanceof Object) {targetObj[key] deepClone(copy);} else {targetObj[key] copy;}} else if (typeof(copy) function) {targetObj[key] eval(copy.toString());} else {targetObj[key] copy;}}return targetObj;}// 定义 sourceObjlet sourceObj {number: 1,string: source1,boolean: true,null: null,undefined: undefined,arr: [{name: arr1}],func: () sourceFunc1,obj: {string: obj1,func: () objFunc1}}// 拷贝sourceObjlet copyObj deepClone(sourceObj);// 修改 sourcecopyObj.number 2;copyObj.string source2;copyObj.boolean false;copyObj.arr[0].name arr2;copyObj.func () sourceFunc2;copyObj.obj.string obj2;copyObj.obj.func () objFunc2;// 执行console.log(sourceObj);/* {number: 1,string: source1,boolean: true,null: null,undefined: undefined,arr: [{name: arr1}],func: () sourceFunc1,obj: {func: () objFunc1,string: obj1}}*/两个方法可以合并在一起/* deep 为 true 表示深复制为 false 表示浅复制* sourceObj 表示源对象* 执行完函数返回目标对象*/function clone (deep true, sourceObj {}) {let targetObj Array.isArray(sourceObj) ? [] : {};let copy;for (var key in sourceObj) {copy sourceObj[key];if (deep typeof(copy) object) {if (copy instanceof Object) {targetObj[key] clone(deep, copy);} else {targetObj[key] copy;}} else if (deep typeof(copy) function) {targetObj[key] eval(copy.toString());} else {targetObj[key] copy;}}return targetObj;}三、使用技巧1. concat()、slice()(1)若拷贝数组是纯数据(不含对象)可以通过concat() 和 slice() 来实现深拷贝let a [1, 2];let b [3, 4];let copy a.concat(b);a[1] 5;b[1] 6;console.log(copy);// [1, 2, 3, 4]let a [1, 2];let copy a.slice();copy[0] 3;console.log(a);// [1, 2](2)若拷贝数组中有对象可以使用 concat() 和 slice() 方法来实现数组的浅拷贝。let a [1, {name: hh1}];let b [2, {name: kk1}];let copy a.concat(b);copy[1].name hh2;copy[3].name kk2;console.log(copy);// [1, {name: hh2}, 2, {name: kk2}]无论 a[1].name 或者 b[1].name 改变copy[1].name 的值都会改变。let a [1, {name: hh1}];let copy a.slice();copy[1].name hh2;console.log(a);// [1, {name: hh2}]改变了 a[1].name 后copy[1].name 的值也改变了。2. Object.assign()、Object.create()Object.assign()、Object.create() 都是一层(根级)深拷贝之下的级别为浅拷贝。(1) 若拷贝对象只有一级可以通过 Object.assign()、Object.create() 来实现对象的深拷贝let sourceObj {str: hh1,number: 10}let targetObj Object.assign({}, sourceObj)targetObj.str hh2console.log(sourceObj);// {str: hh1, number: 10}let sourceObj {str: hh1,number: 10}let targetObj Object.create(sourceObj)targetObj.str hh2console.log(sourceObj);// {str: hh1, number: 10}(2) 若拷贝对象有多级 Object.assign()、Object.create() 实现的是对象的浅拷贝。let sourceObj {str: hh,number: 10,obj: {str: kk1}}let targetObj Object.assign({}, sourceObj)targetObj.obj.str kk2console.log(sourceObj);// {// str: hh,// number: 10,// obj: {// str: kk2// }// }let sourceObj {str: hh,number: 10,obj: {str: kk1}}let targetObj Object.create(sourceObj)targetObj.obj.str kk2console.log(sourceObj);// {// str: hh,// number: 10,// obj: {// str: kk2// }// }修改了 targetObj.obj.str 的值之后sourceObj.obj.str 的值也改变了。3. 对象的解构对象的解构同 Object.assign() 和 Object.create()都是一层(根级)深拷贝之下的级别为浅拷贝。(1)若拷贝对象只有一层可以通过对象的解构来实现深拷贝let sourceObj {str: hh1,number: 10}let targetObj {...sourceObj};targetObj.str hh2console.log(sourceObj);// {str: hh1, number: 10}(2)若拷贝对象有多层通过对象的解构实现的是对象的浅拷贝。let sourceObj {str: hh,number: 10,obj: {str: kk1}}let targetObj {...sourceObj};targetObj.obj.str kk2console.log(sourceObj);// {// str: hh,// number: 10,// obj: {// str: kk2// }// }4. JSON.parse()用 JSON.stringify() 把对象转成字符串再用 JSON.parse() 把字符串转成新的对象可以实现对象的深复制。let source [hh, 1, [2, 3], {name: kk1}];let copy JSON.parse(JSON.stringify(source));copy[2][1] 4;copy[3].name kk2;console.log(source);// [hh, 1, [2, 3], {name: kk1}]可以看出虽然改变了 copy[2].name 的值但是 source[2].name 的值没有改变。JSON.parse(JSON.stringify(obj)) 不仅能复制数组还可以复制对象但是几个弊端1)它会抛弃对象的 constructor深拷贝之后不管这个对象原来的构造函数是什么在深拷贝之后都会变成 Object2)这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象即那些能够被 json 直接表示的数据结构。RegExp 对象是无法通过这种方式深拷贝。3)只有可以转成 JSON 格式的对象才可以这样用像 function 没办法转成 JSON。5. 可以使用的库以下两种库都能实现深浅拷贝有各自的使用方法。jQuery具体使用可以参考官方文档Lodash具体使用可以参考官方文档