JavaScript中的数据类型次要分为根本数据类型和援用数据类型。
常见根本数据类型次要有:undefined, null, 布尔值, 字符串和数值;
援用类型次要是对象(Object)。
要探讨对象的深浅拷贝问题,首先要理解下JS中的堆内存和栈内存的。
栈内存(stack)和堆内存(heap)
栈内存中变量个别都是已知大小或者范畴有下限的,可看做简略存储,给人整齐划一的感觉。次要用于存储各种根本类型的变量,包含Boolean、Number、String、Undefined、Null,以及对象变量的指针。
而堆内存贮存的数据大小,往往都是未知的。对象自身是贮存在堆内存中的。
对堆栈内存数据有点理解之后咱们持续想,当初有obj1这么一个对象,咱们心愿依据obj1拷贝出一个对象obj2来,当初咱们有三大类形式:
- 间接赋值
- 浅拷贝
- 深拷贝
赋值
间接将obj1赋值给obj2,其实赋予的值是obj1的指针,此时的赋值,只是“赋址”。也就是obj1和obj2在栈内存中的值都是obj1的指针,都指向堆内存中的obj1。所以当咱们扭转obj1或obj2的任何一个的数据时,另一个都会扭转(两者其实是同一对象)。
var obj1 = { name: "greennn", age: 25, bestfriend: { name: "xiaoming", age: 23 }};var obj2 = obj1;obj2.age = 20;obj1.bestfriend.name = "xiaohong";console.log( obj1.age ); // 20console.log( obj2.bestfriend.name); //"xiaohong"console.log( obj1 === obj2 ); // true
浅拷贝
浅拷贝会将对象的第一层属性值独立的拷贝给新对象。
浅拷贝能够通过创立一个新的空对象,for...in遍历现有对象的属性增加给新对象;代码更简洁的形式是Object.assign()办法。也能够应用第三方库,如【Underscore】_.clone()等。
咱们先看看第一种形式封装的写法。
function shallowCopy( obj ) { let copy = Array.isArray(obj) ? [] : {}; for (let i in obj) { copy[i] = obj[i]; } return copy;}var obj2 = shallowCopy( obj1 );...
这里咱们用Object.assign()办法实现浅拷贝,展现后果。
当对象只有一层属性时:
var obj1 = { name: "greennn", age: 25};var obj2 = Object.assign( {}, obj1 );obj2.age = 20;console.log( obj1.age ) // 25console.log( obj1 === obj2 ) // false
当初obj1和obj2看上去就是两个独立的对象了,的确起到了“拷贝”的成果。但浅拷贝的“浅”字通知咱们这里必定有陷阱,接下来咱们看看对象的某个属性是对象时的状况。
var obj1 = { name: "greennn", age: 25, bestfriend: { name: "xiaoming", age: 23 }};var obj2 = Object.assign( {}, obj1 );obj2.age = 20;obj1.bestfriend.name = "xiaoding";console.log( obj1.age ) // 25console.log( obj2.bestfriend.name ); // "xiaoding"console.log( obj1 === obj2 ) // false
上例就能看进去,浅拷贝只是复制了一层属性,而且当原对象有多层属性时,第一层的某个属性prop1的值指向一个对象时,理论记录的是指针,复制的值也是该属性值对象的指针,即新旧两个对象的prop1属性记录都是指向同一对象的指针。看上面示意图会更分明一些:
深拷贝
深拷贝是指将每一层的属性都独立拷贝给新对象,实现两个对象的齐全隔离。
深拷贝的实现有以下几个形式:
- 通过JSON转化
这个形式代码简洁,但它存在的问题是只能正确处理 Number, String, Boolean, Array,等扁平对象,即那些可能被 json 间接示意的数据结构。无奈复制对象中的函数,会疏忽对象中的undefined。
function jsonCopy(obj) { return JSON.parse(JSON.stringify(obj));}var obj2 = jsonCopy({ a:1 });
- for···in循环加递归
只是一段简略的示例,性能上其实和下面的JSON一样,在一些非凡状况下会有问题,为了防止踩坑倡议一看而过。
function deepCopy(obj) { let copy = Array.isArray(obj) ? [] : {}; for (let i in obj) { // 遍历时过滤掉原型链上的属性 if (obj.hasOwnProperty(i)) { // 属性是对象时递归解决 copy[i] = typeof obj[i] === "object" ? deepCopy(obj[i]) : obj[i]; } } return copy;}
- lodash _.cloneDeep()
本着不要反复造轮子的准则(除非你能造的比人家好),还是倡议间接应用第三方库lodash的办法。lodash提供的复制办法有两个,_.clone()和_.cloneDeep()。其中_.clone(obj, true)等价于_.cloneDeep(obj)。
var _ = require("lodash");var obj1 = { name: "greennn", age: 25, bestfriend: { name: "xiaoming", age: 23 }};var obj2 = _.cloneDeep( obj1 );obj2.age = 20;obj1.bestfriend.name = "xiaoding";console.log( obj1.age ) // 25console.log( obj2.bestfriend.name ); // "xiaoming"console.log( obj1 === obj2 ) // false
能够看出,通过深拷贝之后,obj1 和 obj2曾经齐全独立,互不干涉了。