前言
-路漫漫其修远兮,我将上下而求索。
在JavaScript数据类型分为基础类型和引用类型,而引用类型又称为对象,可见了解对象是我们真正掌握JavaScript语言的必备技能。本章主要与大家一起去探索JavaScript对象的一些经常被我们忽略的以及难以理解的知识。好了,废话不多说让我们一起进入JavaScript对象的世界吧。
基本概念
- 个人理解:深浅拷贝在JavaScript中只是对于引用类型来说的,因为在JavaScript中基本数据类型是通过值传递的,所以他们不存在深拷贝一说。
- 对象的浅拷贝:即只复制对象在栈内存中的保存的地址值,例如我们直接用赋值符号拷贝另一个对象的值。
- 对象的深拷贝:复制对象存在堆内存中的实际值并且重新在栈内存中生成一份指向新堆内存中的地址值。
- 原因:JavaScript中的引用类型的数据是被存放在堆内存中的,他们在栈内存中的变量只是保存了一个指向对堆内存中值的指针(类似与c中的指针),因此我们在访问引用类型的数据时只能先从栈中取出对象的指针地址,然后才会从堆内存中取得对象的数据。这也是JavaScript对象用赋值符号只能复制指针地址的原因。
为何进行对象深拷贝
- 如果只是对对象数据类型进行简单的浅拷贝,则新复制的值和老值会共用堆内存中的数据,导致值不确定性,例如:
let a={ val:"test" } console.log(a.val)//"test" let b=a b.val="test2" console.log(a.val)//"test2" console.log(b.val)//"test2"
这也是导致前端显示异常的根本原因之一
- 提高代码可维护性,多人合作开发时,共用某份是对象类型的数据如果只进行简单的浅拷贝则很影响他人。
深拷贝的方法
- JSON.stringfy() => JSON.parse() 即将一个对象先解析为json 字符串然后再解析
let a={ val:'test' } let b=JSON.parse(JSON.stringfy(a))
优点:操作简单。
缺点:1.如果对象很大会占用内存 2.对于复杂的对象类型(即属性是诸如 Map, Set, RegExp, Date, ArrayBuffer,function和其他内置类型),在进行序列化时会丢失。 丢失属性。3.无法进行对象的循环处理。
- 使用object.assign()来实现:
let originobj= { 'name': 'test', 'weight': 50 }; let newobj = Object.assign({}, originobj, { 'test': '111' }); newobj.age = 60; console.log('originobj', originobj); //{name: "test", weight: 50} console.log('newobj', newobj); //{name: "test", weight: 50, test: "111", age: 60}
优点:方便快捷 不会丢失原数据 缺点:只能复制简单的对象(),即Object.assign()拷贝的只是属性值 注意:如果对象中的属性还是对象则Object.assign()不会进行值拷贝,只能进行引用拷贝
- 为了解决JSON和assign复制带来的问题,我们可以采用循环递归复制,即在遍历赋值对象属性的时候,遇到属性是引用类型的,则把这个属性展开赋值,如下代码所示:
function cloneDeep(newobj={},originObj){ for(let key in originObj){ //判断是否是对象类型 if(originObj[key] && originObj[key] instanceof object){ //递归将对象类型赋值给新数组 newobj[key] = cloneDeep(originObj[key]) }else{ newobj[key] = originObj[key] } } return newobj } let obj = {a:{b:'test'},c:"test2"} //此时完全copy了obj的值 let obj1 = cloneDeep(obj)//{a:{b:'test'},c:"test2"}
总结
在编程的世界中熟悉某一门语言的基本要求就是掌握这门语言定义的数据类型,不同的语言中所定义的数据类型往往不同。这也是我们同时掌握多中编程语言的难点。但幸运的是他们底层设计原理都是相互借鉴的,这也成为了我们能够掌握夺门语言的关键点之一。