安阳做网站的公司有哪些,青岛seo百科,51ape是谁做的网站,网络服务器机柜厂家前言
对于前端同学来说#xff0c;对象的深拷贝和浅拷贝可以说是面试中最火热的题目之一了#xff0c;今天我们一起来把它盘明白。
1、深拷贝和浅拷贝介绍
深拷贝和浅拷贝都是对对象进行拷贝#xff0c;其主要区别是#xff0c;在对象拷贝时#xff0c;对引用数据类型的处…前言对于前端同学来说对象的深拷贝和浅拷贝可以说是面试中最火热的题目之一了今天我们一起来把它盘明白。1、深拷贝和浅拷贝介绍深拷贝和浅拷贝都是对对象进行拷贝其主要区别是在对象拷贝时对引用数据类型的处理方式。1.1 浅拷贝Shallow Copy浅拷贝是指只复制对象本身和对象中的基本数据类型对于引用类型的属性只复制内存地址引用不复制引用的对象本身。浅拷贝的特点只拷贝第一层。对于基本数据类型拷贝值。对于引用数据类型拷贝引用即新对象和原对象共享引用类型的属性。1.2 深拷贝Deep Copy深拷贝是指创建一个对象这个对象的内容和原始对象完全相同但它们是存储在不同的内存地址上的这意味着我们修改新对象原始对象不受影响。深拷贝的特点拷贝所有层级对多层的属性进行循环递归拷贝。对于基本数据类型拷贝值。和浅拷贝相同对于引用数据类型创建一个新的引用对象并循环拷贝到新对象中也就是新对象和原对象分别引用独立的引用数据不共享引用。1.3 示例说明举个例子constobj{a:1,b:[1,2,3],c:{d:4,e:5,}}constnewObj1shallowClone(obj);console.log(newObj1:,newObj1);constnewObj2deepClone(obj);console.log(newObj2:,newObj2);/** 打印结果 newObj1: { a: 1, b: [ 1, 2, 3 ], c: { d: 4, e: 5 } } newObj2: { a: 1, b: [ 1, 2, 3 ], c: { d: 4, e: 5 } } */我这里并没有给出shallowClone和deepClone的实现代码后文会分别详细介绍。从打印结果来看newObj1和newObj2都是相同的而不同的是对于浅拷贝来说obj.b newObj1.b结果为true。obj.c newObj1.c结果为true。而对于深拷贝来说它们前后都是不相等的。obj.b newObj.b结果为false。obj.c newObj.c结果为false。2、浅拷贝的实现方式2.1 展开运算符可以用ECMAScript 2015新增特性也就是ES6提供的语法三个点...展开运算符它可以展开可迭代对象数组、字符串、Map、Set 等从而来实现浅拷贝具体用法如下constnewObj{...obj};2.2 Object.assignObject.assign是Object类自带的一个静态方法可以将一个或多个对象中的可枚举此属性的enumerable为true自有属性对象自身的从原型上继承的不算合并到目标对象中。constnewObjObject.assign({},obj1 obj2,obj3,...objN);2.3 for…in Object.prototype.hasOwnProperty直接用for..in循环配合Object.prototype.hasOwnProperty判断是否是自身的属性来进行拷贝。functionshallowClone(obj){constnewObj{};for(letkeyinobj){if(obj.hasOwnProperty(key)){newObj[key]obj[key];}}returnnewObj;}constobj{a:1,b:[1,2,3],c:{d:4,e:5,}};constnewObjshallowClone(obj);console.log(newObj)或者用Object.keys先拿到自身的属性的数组然后forEach循环拷贝也可方式有很多种大家可以自行扩展。2.4 扩展展开运算符和Object.assign在实现浅拷贝上有什么区别展开运算符和Object.assign虽然可以都实现浅拷贝但仍有细微的区别比如在遇到getter和setter时两者的表现不一样。来举个例子首先是展开运算符:constobj1{geta(){console.log(getter)return1;},seta(val){console.log(setter)}}constobj2{a:2,}constnewObj1{...obj1,...obj2};console.log(newObj1);console.log(newObj1.a)console.log(------------------------);constnewObj2{...obj2,...obj1};console.log(newObj2);console.log(newObj2.a)/** * 打印结果 getter { a: 2 } 2 ------------------------ getter { a: 1 } 1 */从打印结果可以看出展开运算符在拷贝时有如下特点合并时不会执行setter合并后取值时会按照合并的先后顺序后合并的值优先级更高。然后是Object.assignconstobj1{geta(){console.log(getter)return1;},seta(val){console.log(setter)}}constobj2{a:2,}constnewObjObject.assign(obj1,obj2);console.log(newObj);console.log(newObj.a)console.log(------------------------);constnewObj1Object.assign(obj2,obj1);console.log(newObj1);console.log(newObj1.a);/** * 打印结果 setter { a: [Getter/Setter] } getter 1 ------------------------ getter { a: 1 } 1 */从打印结果可以看出Object.assign在拷贝时有如下特点同名属性和同名getter、setter合并时会执行setter而同名getter、setter和同名属性合并时却不会执行setter。无论同名属性和同名getter和setter的合并先后顺序如何最终访问只会访问到getter里面的值只是从控制台里看的效果不一样而已。3、深拷贝的实现方式3.1 JSON.parse(JSON.stringify(obj))JSON.stringify将一个对象序列化成一个JSON字符串包括嵌套的对象属性。JSON.parse将一个JSON反序列化成为一个 JS 对象。由于在内存中JSON 字符串的地址都是独立的和原始对象不是同一个地址所以我们就能通过JSON.parse解析出一个新对象了。下面看一下用这种方式实现深拷贝的优缺点优点简单易用语法JSON.parse(JSON.stringify(obj))用起来非常简单。跨平台在不同平台和环境都能用甚至是其它语言也有提供对应的实现比如Java。兼容性好各大浏览器都支持。缺点无法处理特殊对象类型比如函数、正则表达式、日期对象等拷贝的时候会丢失函数和 undefined。时间对象 Date 会变成字符串形式。RegExp、Error 对象会变成空对象。NaN、Infinity、-Infinity会变成 null。等等…。无法处理循环引用比如在一个对象中a引用了bb引用了c而c又引用了a出现这种情况调用JSON.parse(JSON.stringify(obj))会报错。3.2 借助第三方库实现深拷贝一般我们会借助第三方库实现比如lodashlodash提供了一个cloneDeep的方法实现深拷贝。const_require(lodash);constobj{a:[{b:2}]};constres_.cloneDeep(obj);console.log(res);// 输出{ a: [ { b: 2 } ] }3.3 手撸一个深拷贝方法深拷贝其实实现起来要写完整还是挺复杂的要处理函数、数组、正则甚至是symbol、buffer等但对于面试来说我们写个简单版本就行啦。手动实现深拷贝有两个关键点对象是以key和value键值对的方式存储的所以要拷贝它们必须要用循环。既然要深拷贝相较于只拷贝最外层的浅拷贝就需要用递归或循环拷贝N层。废话不多说直接上完整代码。constisObj(target)typeoftargetobjecttarget!nullfunctiondeepClone(obj,hashnewWeakMap()){if(!isObj(obj))returnobjif(hash.has(obj))returnhas.get(obj)consttargetnewobj.constructor()hash.set(obj,target)Object.keys(obj).forEach((key){target[key]deepClone(obj[key],hash)})returntarget}constobj{a:[{b:2}]}constresdeepClone(obj)console.log(res)// 输出{ a: [ { b: 2 } ] }我们用一个WeakMap来处理循环引用然后通过拿到对象引用的constructor来复制对象这样我们就省去了判断不同对象类型这一步会简单很多然后forEach循环递归复制就好啦。虽然可能有人会说用constructor比较粗糙但这是比较简洁的写法我们面试的时候大可不必这么较真只要知道它的核心思路和原理就行啦4、如何选择浅拷贝和深拷贝浅拷贝能共享数据节约内存性能高。深拷贝实现数据隔离数据更安全。但要注意循环引用问题、特殊对象的处理以及对象层级过深带来的性能问题。在平常开发中数据拷贝几乎设计不到性能问题所以如果不介意引用数据共享选浅拷贝需要引用数据相互独立选深拷贝。小结先介绍了深拷贝深拷贝和浅拷贝的区别。它们的主要区别在于拷贝时对引用数据类型的处理浅拷贝是共享引用而深拷贝是复制出一个新对象和原对象相互独立没任何关系。然后介绍了浅拷贝和深拷贝的主要实现方式。浅拷贝的实现方式主要有展开运算符、Object.assign、for..in Object.prototype.hasOwnProperty等还扩展介绍了展开运算符和Object.assign的区别主要是体现在复制时对属性访问器getter和setter的处理方式不同而深拷贝的实现方式主要有JSON.parse(JSON.stringify(obj))、借助第三方库比如 lodash、手撸一个深拷贝方法注意对循环引用的处理。最后介绍了在实际开发中如何选择浅拷贝和深拷贝。主要选择方式是不介意引用数据共享选浅拷贝需要引用数据相互独立选深拷贝。