淺拷貝java
複製一層對象的屬性,並不包括對象裏面的爲引用類型的數據,當改變拷貝的對象裏面的引用類型時,源對象也會改變。數組
深拷貝瀏覽器
從新開闢一個內存空間,須要遞歸拷貝對象裏的引用,直到子屬性都爲基本類型。兩個對象對應兩個不一樣的地址,修改一個對象的屬性,不會改變另外一個對象的屬性。spa
javaScript的變量類型指針
(1)基本類型:
5種基本數據類型Undefined、Null、Boolean、Number 和 String,變量是直接按值存放的,存放在棧內存中的簡單數據段,能夠直接訪問。code
(2)引用類型:
存放在堆內存中的對象,變量保存的是一個指針,這個指針指向另外一個位置。當須要訪問引用類型(如對象,數組等)的值時,首先從棧中得到該對象的地址指針,而後再從堆內存中取得所需的數據。對象
簡述兩種類型的存儲方式:blog
a. 基本類型(深拷貝)--名值存儲在棧內存中,例如let a=1;遞歸
棧內存 | |
a | 1 |
當b=a複製時,棧內存會開闢一個內存ip
棧內存 | |
a | 1 |
b | 1 |
因此當你此時修改a=2,對b並不會形成影響。固然,let a=1,b=a;雖然b不受a影響,但這也算不上深拷貝,由於深拷貝自己只針對較爲複雜的object類型數據。
b.引用數據類型(淺拷貝)--名存在棧內存中,值存在於堆內存中,可是棧內存會提供一個引用的地址指向堆內存中的值
棧內存 | 堆內存 | ||
name | val | val | |
a | 堆地址1 |
[0,1,2] |
當b=a進行拷貝時,其實複製的是a的引用地址,而並不是堆裏面的值。
而當咱們a[0]=1時進行數組修改時,因爲a與b指向的是同一個地址,因此天然b也受了影響,這就是所謂的淺拷貝了。
淺拷貝的實現
1)簡單的引用複製
function shallowClone(copyObj) { var obj = {}; for (var i in copyObj) { obj[i] = copyObj[i]; } return obj; } var x = { a: 1, b: { f: { g: 1 } }, c: [1, 2, 3] }; var y = shallowClone(x); x.b.f.g = 6 console.log(y.b.f.g); // 6
2)Object.assign() 方法能夠把任意多個的源對象自身的可枚舉屬性拷貝給目標對象,而後返回目標對象
let obj1 = { a: 0, b: { c: 0 } }; let obj2 = Object.assign({}, obj1); console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}} obj2.b.c = 3; console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 3}} console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 3}}
深拷貝的實現
1)Array的slice和concat方法
Array的slice和concat方法不修改原數組,只會返回一個淺複製了原數組中的元素的一個新數組。之因此把它放在深拷貝里,是由於它看起來像是深拷貝。而實際上它是淺拷貝。
let a = [0, 1, [2, 3], 4], b = a.slice(); a[0] = 1; a[2][0] = 1; console.log(a); // [1,1,[1,3],4] console.log(b); // [0,1,[1,3],4]
從以上示例中能夠看出拷貝的不完全,b對象的一級屬性確實不受影響了,可是二級屬性仍是沒能拷貝成功,仍然脫離不了a的控制,說明slice根本不是真正的深拷貝。
第一層的屬性確實深拷貝,擁有了獨立的內存,但更深的屬性卻仍然公用了地址,因此纔會形成上面的問題。
同理,concat方法與slice也存在這樣的狀況,他們都不是真正的深拷貝,這裏須要注意。
2)利用遞歸複製全部層級屬性,實現深拷貝
function deepClone(obj) { let objClone = Array.isArray(obj) ? [] : {}; if (obj && typeof obj === "object") { for (key in obj) { 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] = 2; console.log(a);// [2,2,3,4] console.log(b);// [1,2,3,4]
3)jQuery.extend()方法源碼實現
$.extend( [deep ], target, 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)// [1,1,[1,3],4] console.log(b)// [0,1,[2,3],4]
注意:jQuery的extend方法使用基本的遞歸思路實現了淺拷貝和深拷貝,可是這個方法也沒法處理源對象內部循環引用,例如:
var a = { "name": "aaa" }; var b = { "name": "bbb" }; a.child = b; b.parent = a; $.extend(true, {}, a);//直接報了棧溢出。Uncaught RangeError: Maximum call stack size exceeded
4)JSON對象的parse和stringify
JSON對象是ES5中引入的新的類型(支持的瀏覽器爲IE8+),JSON對象parse方法能夠將JSON字符串反序列化成JS對象,stringify方法能夠將JS對象序列化成JSON字符串,藉助這兩個方法,也能夠實現對象的深拷貝。
obj1 = { a: 0, b: { c: 0 } }; let obj3 = JSON.parse(JSON.stringify(obj1)); obj1.a = 4; obj1.b.c = 4; console.log(JSON.stringify(obj3)); // { a: 0, b: { c: 0}} console.log(JSON.stringify(obj1)); // { a: 4, b: { c: 4}}