摘要:那么如何切斷和之間的關系呢,可以拷貝一份的數據,根據拷貝的層級不同可以分為淺拷貝和深拷貝,淺拷貝就是只進行一層拷貝,深拷貝就是無限層級拷貝。
深拷貝 vs 淺拷貝
深拷貝和淺拷貝都是針對的引用類型,JS中的變量類型分為值類型(基本類型)和引用類型;對值類型進行復制操作會對值進行一份拷貝,而對引用類型賦值,則會進行地址的拷貝,最終兩個變量指向同一份數據。
// 基本類型 var a = 1; var b = a; a = 2; console.log(a, b); // 2, 1 ,a b指向不同的數據 // 引用類型指向同一份數據 var a = {c: 1}; var b = a; a.c = 2; console.log(a.c, b.c); // 2, 2 全是2,a b指向同一份數據
對于引用類型,會導致a b指向同一份數據,此時如果對其中一個進行修改,就會影響到另外一個,有時候這可能不是我們想要的結果,如果對這種現象不清楚的話,還可能造成不必要的bug。那么如何切斷a和b之間的關系呢,可以拷貝一份a的數據,根據拷貝的層級不同可以分為淺拷貝和深拷貝,淺拷貝就是只進行一層拷貝,深拷貝就是無限層級拷貝。
深復制和淺復制最根本的區別在于是否是真正獲取了一個對象的復制實體,而不是引用
深復制在計算機中開辟了一塊內存地址用于存放復制的對象,
淺復制僅僅是指向被復制的內存地址,如果原地址中對象被改變了,那么淺復制出來的對象也會相應改變。
var a1 = {b: {c: {}}; var a2 = shallowClone(a1); // 淺拷貝 a2.b.c === a1.b.c // true var a3 = clone(a1); // 深拷貝 a3.b.c === a1.b.c // false
所謂的淺復制,只是拷貝了基本類型的數據,而引用類型數據,復制后也是會發生引用,我們把這種拷貝叫做“(淺復制)淺拷貝”。Object.assign({}, obj1, obj2)
function shallowClone(source) { var target = {}; for(var i in source) { if (source.hasOwnProperty(i)) { target[i] = source[i]; } } return target; }
深拷貝的問題其實可以分解成兩個問題,淺拷貝+遞歸,什么意思呢?假設我們有如下數據
var a1 = {b: {c: {d: 1}};
只需稍加改動上面淺拷貝的代碼即可,注意區別
function clone(source) { var target = {}; for(var i in source) { if (source.hasOwnProperty(i)) { if (typeof source[i] === "object") { target[i] = clone(source[i]); // 注意這里 } else { target[i] = source[i]; } } } return target; }
其實上面的代碼問題太多了,先來舉幾個例子吧
沒有對參數做檢驗
function isObject(x) { return Object.prototype.toString.call(x) === "[object Object]"; } function clone(source) { if (!isObject(source)) return source; // xxx }
判斷是否對象的邏輯不夠嚴謹
沒有考慮數組的兼容
遞歸方法最大的問題在于爆棧,當數據的層次很深是就會棧溢出
方法: 用系統自帶的JSON來做深拷貝的例子,下面來看下代碼實現
function cloneJSON(source) { return JSON.parse(JSON.stringify(source)); }
但是cloneJSON也有缺點,它的內部也是使用遞歸的方式
cloneJSON(createData(10000)); // Maximum call stack size exceeded
既然是用了遞歸,那循環引用呢?并沒有因為死循環而導致棧溢出啊,原來是JSON.stringify內部做了循環引用的檢測,正是我們上面提到破解循環引用的第一種方法:循環檢測
var a = {}; a.a = a; cloneJSON(a) // Uncaught TypeError: Converting circular structure to JSON
如何復制對象而不發生引用呢,對于數組,ES6我們復制有新的兩種方法,不會發生引用。
第一種:Array.from(要復制的數組);
var arr1=[1,2,3]; var arr2=Array.from(arr1); arr1.push(4); alert(arr1); //1234 alert(arr2); //123 arr2.push(5); alert(arr1); //1234 alert(arr2); //1235
第二種:...
var arr1=[1,2,3]; var arr2=[...arr1]; arr1.push(4); alert(arr1); //1234 alert(arr2); //123 arr2.push(5); alert(arr1); //1234 alert(arr2); //1235
第二種這個方法也可以用在函數的行參上面。
function show(...arr1){ //直接來復制arguments這個偽數組,讓它變成真正的數組,從而擁有數組的方法。 alert(arr1); //1234 arr1.push(5); alert(arr1); //12345 } show(1,2,3,4)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/100128.html
摘要:深拷貝淺拷貝本文主要對深拷貝淺拷貝的解釋及實現做一下簡單記錄。之所以會有深拷貝與淺拷貝之分,是因為不同數據類型的數據在內存中的存儲區域不一樣。但注意,只能做一層屬性的淺拷貝。 深拷貝VS淺拷貝 本文主要對深拷貝&淺拷貝的解釋及實現做一下簡單記錄。原文鏈接,歡迎star。 之所以會有深拷貝與淺拷貝之分,是因為不同數據類型的數據在內存中的存儲區域不一樣。 堆和棧是計算機中劃分出來用來存儲的...
摘要:而在這個運算符的相關用例中,往往會涉及到其他知識點,深拷貝和淺拷貝就是其中之一。即對象的淺拷貝會對主對象的值進行拷貝,而該值有可能是一個指針,指向內存中的同一個對象。,可以看到深拷貝和淺拷貝是對復制引用類型變量而言的。 在ES6的系列文章中,基本都會提到Spread——擴展運算符(...)。而在這個運算符的相關用例中,往往會涉及到其他知識點,深拷貝和淺拷貝就是其中之一。 背景知識 在討...
閱讀 2902·2021-11-25 09:43
閱讀 2320·2021-11-24 09:39
閱讀 2708·2021-09-23 11:51
閱讀 1400·2021-09-07 10:11
閱讀 1449·2019-08-27 10:52
閱讀 1929·2019-08-26 12:13
閱讀 3356·2019-08-26 11:57
閱讀 1393·2019-08-26 11:31