深拷贝和浅拷贝
1.基本类型的值和引用类型的值
基本类型值指的是存储在栈中的一些简单的数据段,在JavaScript中基本数据类型有String,Number,Undefined,Null,Boolean,在ES6中,又定义了一种新的基本数据类型Symbol,所以一共有6种
基本类型是按值访问的,从一个变量复制基本类型的值到另一个变量后这2个变量的值是完全独立的,即使一个变量改变了也不会影响到第二个变量
2.数据传递的方法:
值传递:基本数据类型的数据不会发改变,因为基本数据类型一般存放在栈里面,值传递只是将数据拷贝了一份给另一个变量
var a = 10; var b = a; b+=10; console.log(a);//10 console.log(b);//20;
引用传递:会改变内存中的数据,因为引用类型的数据都存放在堆里面,栈里面存放的是索引,拷贝的时候是拷贝的地址也就是索引
var arr = [10,20,30,40]; var newArr = arr; newArr[0] = 80; console.log(arr);//[80,20,30,40] console.log(newArr);//[80,20,30,40];
3.深拷贝和浅拷贝
浅拷贝:所谓的浅拷贝就是复制一份引用数据类型的地址,当改变了内存中数据的某一个值得话,也会影响到另一个对象
深拷贝:所谓的深拷贝就是复制一份引用数据类型的数据,当改变了数据的某一个值得话,不会影响到另一个对象(注意深拷贝是拷贝的数据,而不是索引,浅拷贝拷贝的是索引而不是数据)
区别:假设B复制了A,修改A的时候,看B是否发生变化:如果B变了便是浅拷贝,如果B没有变便是深拷贝
以下是一些JavaScript提供的浅拷贝方法:
3.1 Object.assign()
Object.assign(target, ...sources)
var target = {}; var source = {a:1}; Object.assign(target ,source); console.log(target); //{a:1} source.a = 2; console.log(source); //{a:2} console.log(target); //{a:1}
Object.assign是一个浅拷贝,它只是在根属性(对象的第一层级)创建了一个新的对象,但是对于属性的值是仍是对象的话依然是浅拷贝
Object.assign还有一些注意的点是:
- 不会拷贝对象继承的属性
- 不可枚举的属性
- 属性的数据属性/访问器属性
- 可以拷贝Symbol类型
可以理解为Object.assign就是使用简单的=来赋值,遍历从右往左遍历源对象(sources)的所有属性用 = 赋值到目标对象(target)上
3.2 扩展运算符
var cloneObj = { ...obj };
var obj = {a:1,b:{c:1}} var obj2 = {...obj}; obj.a=2; console.log(obj); //{a:2,b:{c:1}} console.log(obj2); //{a:1,b:{c:1}} obj.b.c = 2; console.log(obj); //{a:2,b:{c:2}} console.log(obj2); //{a:1,b:{c:2}}
3.3 Array.prototype.slice()
slice() 方法返回一个新的数组对象,这一对象是一个由 begin和 end(不包括end)决定的原数组的浅拷贝。原始数组不会被改变。
在ES6以前,没有剩余运算符,Array.from的时候可以用 Array.prototype.slice将arguments类数组转为真正的数组,它返回一个浅拷贝后的的新数组
Array.prototype.slice.call({0: "aaa", length: 1}) //["aaa"] let arr = [1,2,3,4] console.log(arr.slice() === arr); //false
深拷贝
将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象
JSON.stringify()
- 拷贝的对象的值中如果有函数,undefined,symbol则经过JSON.stringify()序列化后的JSON字符串中这个键值对会消失
- 无法拷贝不可枚举的属性,无法拷贝对象的原型链
- 拷贝Date引用类型会变成字符串
- 拷贝RegExp引用类型会变成空对象
- 对象中含有NaN、Infinity和-Infinity,则序列化的结果会变成null
- 无法拷贝对象的循环应用(即obj[key] = obj)
function Obj() { this.func = function () { alert(1) }; this.obj = {a:1}; this.arr = [1,2,3]; this.und = undefined; this.reg = /123/; this.date = new Date(0); this.NaN = NaN this.infinity = Infinity this.sym = Symbol(1) } var obj1 = new Obj(); Object.defineProperty(obj1,'innumerable',{ enumerable:false, value:'innumerable' }) console.log('obj1',obj1); var str = JSON.stringify(obj1); var obj2 = JSON.parse(str); console.log('obj2',obj2);
虽说通过JSON.stringify()方法深拷贝对象也有很多无法实现的功能,但是对于日常的开发需求(对象和数组),使用这种方法是最简单和快捷的
使用第三方库实现对象的深拷贝
1.lodash
2.jQuery
自己来实现一个深拷贝函数
递归
var obj1 = { a:{ b:1 } }; function deepClone(obj) { var cloneObj = {}; //在堆内存中新建一个对象 for(var key in obj){ //遍历参数的键 if(typeof obj[key] ==='object'){ cloneObj[key] = deepClone(obj[key]) //值是对象就再次调用函数 }else{ cloneObj[key] = obj[key] //基本类型直接复制值 } } return cloneObj } var obj2 = deepClone(obj1); obj1.a.b = 2; console.log(obj2); //{a:{b:1}}
但是还有很多问题
- 首先这个deepClone函数并不能复制不可枚举的属性以及Symbol类型
- 这里只是针对Object引用类型的值做的循环迭代,而对于Array,Date,RegExp,Error,Function引用类型无法正确拷贝
- 对象循环引用成环了的情况
可参考下面的 对象深拷贝和浅拷贝 作者自己实现的深拷贝
posted on 2020-03-04 13:53 yemiaomiao 阅读(174) 评论(0) 编辑 收藏 举报