论基本数据类型与引用数据类型以及深浅拷贝的区别
一、数据类型
- Js有两种数据类型
-
- 基本数据类型:Number、String、Boolean、Null、undefined、Symbol(ES6)等;
- 引用数据类型:Object(数据、数组、函数、正则表达式等,除基本数据类型以外都是对象)
- 基本数据类型与引用数据类型的区别:
2. 栈(stack)与 堆(heap)的概念
-
- 栈:自动分配的内存空间,由系统自动释放;
- 堆:动态分配的内存空间,内存大小不一样,大小也不一定会自动释放;
3. 这两类数据类型的存储方式
-
- 基本数据类型:
var a = 1;var b = a;a = 2;console.log(a,b)
输出结果:
- 基本数据类型:
变a的同时b没有发生改变,因此接下来分析一下产生这种变化的原因:
-
-
-
- 基本数据类型-----名与值存放在栈内存中,例如:let a = 1;
-
-
当 b = a 时,栈会新开辟一个内存,如下:
所以当你此时修改a=2,对b并不会造成影响,因为此时的b已自食其力,翅膀硬了,不受a的影响了。
当然,let a=1,b=a;虽然b不受a影响,但这也算不上深拷贝,因为深拷贝本身只针对较为复杂的object类型数据。
-
- 引用数据类型:
- 浅拷贝
var a = [0,1,2,3]; var b = a; a[0] = 22; console.log(a,b)
输出结果:
- 浅拷贝
- 引用数据类型:
变a的同时b也发生了改变,因此接下来分析一下产生这种变化的原因:
-
-
-
- 引用数据类型-----名存放在栈内存中,值存放在堆内存中,但是栈内存会提供一个 “引用的地址”(即指针) 指向堆内存当中的值,对上述的 “浅拷贝” 进行解析:(复制对象时并不会在堆内存中新生成一个一模一样的对象,只是多了一个保存指向这个对象指针的变量罢了)
-
-
当b拷贝a时,即 b = a ,复制的是a的引用地址,而不是堆内存里面的值;
而当我们 a[0] = 22 进行数组修改时,由于a与b指向的是同一个地址,因此b也受了影响,这就是所谓的 “浅拷贝”;
但是当我们在堆内存中也去开辟一个新的内存,用来专门为b存放值,就像基本类型那样,岂不就达到深拷贝的效果了;
4、“深拷贝”的实现方式
-
- 深拷贝作用:深拷贝在实际开发中是非常有用的:例如后台返回了一堆数据,你需要对这堆数据做操作,但多人开发情况下,你是没办法明确这堆数据是否有其它功能也需要使用,直接修改可能会造成隐性问题,深拷贝能帮你更安全安心的去操作数据。
- 用递归去实现 “深拷贝”:
- 封装一个 “深拷贝” 函数,用递归去复制所有的层级属性:
// 一、递归 function deepClone(obj) { let objClone = Array.isArray(obj) ? [] : {}; if (obj && typeof obj === "object") { for (var key in obj) {//key是属性 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], b = deepClone(a); a[0] = 22; console.log(a, b);
输出结果为:
- 封装一个 “深拷贝” 函数,用递归去复制所有的层级属性:
-
- 通过JSON对象实现 “深拷贝” :
var objClone = JSON.parse(JSON.stringify( obj ));
缺点:丢失constructor,RegExp无法实现
-
- 通过jQuery的extend方法实现 “深拷贝”:
// $.extend( [deep ], target Object, [ object1 , objectN ] ) // deep:表示是否深拷贝,为true为深拷贝,为false,则为浅拷贝 // target Object:表示目标对象,其他对象的成员属性将被附加到该对象上。 // object1,objectN:表示可选,Object类型(即要复制的对象)第一个以及第N个被合并的对象 let a = [0, 1, [2, 3], 4], b = $.extend(true, [], a); a[0] = 1; a[2][0] = 1; console.log(a, b);
- 通过jQuery的extend方法实现 “深拷贝”:
输出结果为:
-
- 通过Object.assign()与 slice() 来实现 “深拷贝”(根本不是真正的深拷贝):concat()等函数也是
- 当对象中只有一级属性,没有二级属性的时候,Object.assign()方法为深拷贝,但是对象中有对象的时候,此方法,在二级属性以后就是浅拷贝。
//进行拷贝 let obj1 = { a: 0, b: { c: 0 } }; let obj2 = Object.assign({}, obj1);//Object.assign(target,sources)target指目标对象,sources指多个源对象 console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}
// 当改变值时 obj1.b.c = 2; console.log(JSON.stringify(obj2))
- 当对象中只有一级属性,没有二级属性的时候,Object.assign()方法为深拷贝,但是对象中有对象的时候,此方法,在二级属性以后就是浅拷贝。
- 通过Object.assign()与 slice() 来实现 “深拷贝”(根本不是真正的深拷贝):concat()等函数也是
输出结果为:
-
-
- 当数组只是一维数组时,可以通过 slice() 方法实现深拷贝,但是存在二维数组时,二维数组中就是浅拷贝;
- 一维数组:
let arr = [0,1,2,3,4]; b = arr.slice(); console.log("复制后的b:" + b); arr[0] = 22; console.log("当值改变时的arr:" + arr) console.log("值变后b的值:" + b)
输出结果为:
- 一维数组:
- 当数组只是一维数组时,可以通过 slice() 方法实现深拷贝,但是存在二维数组时,二维数组中就是浅拷贝;
-
-
-
-
- 二维数组:
let arr = [0,1,2,3,4,[5,6]]; b = arr.slice(); console.log("复制后的b:" + b); arr[0] = 22; arr[5][0] = 55; console.log("当值改变时的arr:" + arr) console.log("值变后b的值:" + b)
输出结果:
- 二维数组:
-
-
-
- lodash函数库实现 “深拷贝”:
lodash是一个很热门的函数库,提供了 lodash.cloneDeep()实现深拷贝,但是一旦拷贝一些很复杂的对象就有可能报错。比如用cloneDeep克隆一个vue实例,就有可能报key.charAt is not a Function的错。(正确的拷贝方法是Vue.extend())。
二、参考地址
https://www.cnblogs.com/c2016c/articles/9328725.html
https://www.cnblogs.com/echolun/p/7889848.html