摘要:而在這個(gè)運(yùn)算符的相關(guān)用例中,往往會涉及到其他知識點(diǎn),深拷貝和淺拷貝就是其中之一。即對象的淺拷貝會對主對象的值進(jìn)行拷貝,而該值有可能是一個(gè)指針,指向內(nèi)存中的同一個(gè)對象。,可以看到深拷貝和淺拷貝是對復(fù)制引用類型變量而言的。
在ES6的系列文章中,基本都會提到Spread——擴(kuò)展運(yùn)算符(...)。而在這個(gè)運(yùn)算符的相關(guān)用例中,往往會涉及到其他知識點(diǎn),深拷貝和淺拷貝就是其中之一。
背景知識在討論深拷貝和淺拷貝之前,我們先看一個(gè)例子:
let a = "hi"; b = a; b = "hello"; console.log(a); // "hi" let arr1 = [1,2,3]; arr2 = arr1; arr2[0] = 0; console.log(arr1); // [0,2,3]
可以看到:為不同的js變量復(fù)制值時(shí),結(jié)果是不同的。把字符串a的值復(fù)制給b,改變b的值不會影響a的值,而把數(shù)組arr1的值復(fù)制給arr2時(shí),改變arr2的值,arr1的值也跟著改變了。
這是因?yàn)閖s存在兩種不同數(shù)據(jù)類型的值:基本類型值和引用類型值。
在訪問這兩種類型的變量時(shí),其訪問方式是不同的。在這里,先記一下結(jié)論:
基本數(shù)據(jù)類型是按值訪問的
引用數(shù)據(jù)類型是按引用訪問的
(實(shí)際上這種說法并不嚴(yán)密,為便于理解,我們先這么記)
什么意思?
JavaScript不允許直接訪問內(nèi)存中的位置,換句話說,不能直接操作對象的內(nèi)存空間。
因此,在操作對象時(shí),我們實(shí)際上是在操作對象的引用,而不是實(shí)際的對象。
從一個(gè)變量向另一個(gè)變量復(fù)制值時(shí)(不管是復(fù)制基本類型還是引用類型),都會先為這個(gè)新變量分配一個(gè)空間,然后把該值復(fù)制到新創(chuàng)建的這個(gè)空間里。
不同的是,在復(fù)制引用類型的值時(shí),實(shí)際上復(fù)制過去的是一個(gè)指針,示例圖如下:
在js中,除了7種基本類型外,其他的都是引用類型,比如Object,Array,Function,所以不難理解:
let obj1 = {name:"hx",age:18}; let obj2 = obj1; obj2.age = 0; console.log(obj1); // {name:"hx",age:0}
“引用類型的值是按引用訪問的”不嚴(yán)密在:當(dāng)復(fù)制保存著對象的某個(gè)變量時(shí),操作的是對象的引用;而當(dāng)為對象添加屬性時(shí),操作的是實(shí)際的對象。 ——靈圖社區(qū)深拷貝 vs. 淺拷貝
我們先來看一下概念:
淺拷貝:
被復(fù)制對象的所有變量都含有與原來的對象相同的值,而所有的對其他對象的引用仍然指向原來的對象。即對象的淺拷貝會對主對象的值進(jìn)行拷貝,而該值有可能是一個(gè)指針,指向內(nèi)存中的同一個(gè)對象。深拷貝:
深拷貝不僅將原對象的各個(gè)屬性逐個(gè)復(fù)制出去,而且將原對象各個(gè)屬性所包含的對象也依次采用深復(fù)制的方法遞歸復(fù)制到新對象上。所以對一個(gè)對象的修改完全不會影響到另一個(gè)對象。
OK,可以看到深拷貝和淺拷貝是對“復(fù)制引用類型變量”而言的。事實(shí)上,也只有在引用類型中才有討論兩者區(qū)別的意義,對于基本數(shù)據(jù)類型,怎么拷都是“深拷貝”。
淺拷貝就不說了,=就是淺拷貝,那么如何實(shí)現(xiàn)深拷貝呢?
對于Object和Array兩種類型,我們分別舉例:
首先是assign(),看代碼:
let obj = {name:"hx",age:18}; let copyObj = Object.assign({},obj); copyObj.name = "H.Lucas"; console.log(obj); // {name:"hx", age:18}
Emm,貌似是深拷貝哈,那要是二維對象呢?
let obj = {name:"zj",attr:{age:18, nickname:"Z.Crystal"}} let copyObj = Object.assign({},obj); copyObj.attr.nickname = "erni"; console.log(obj); // {name:"zj",attr:{age:18, nickname:"erni"}}
好吧,翻車了,看來assign只能實(shí)現(xiàn)一維對象的深拷貝。
然后是擴(kuò)展運(yùn)算符...,看代碼:
let obj = {name:"hx",age:18}; let copyObj = {...obj}; copyObj.name = "H.Lucas"; console.log(obj); // {name:"hx", age:18}
嗯,深拷貝哈,也來個(gè)二維對象試試:
let obj = {name:"zj",attr:{age:18, nickname:"Z.Crystal"}} let copyObj = {...obj}; copyObj.attr.nickname = "erni"; console.log(obj); // {name:"zj",attr:{age:18, nickname:"erni"}}
好吧,也炸了,看來都實(shí)現(xiàn)不了多維對象的深拷貝。
不過這里還是推崇一下...,為什么?看兩段代碼:
let obj1 = {a:1,b:2} let obj2 = {b:3,c:4} // 構(gòu)建一個(gè)新對象obj,值是obj1和obj2的集合 let obj = Object.assign(obj1,obj2) obj.b = 100 console.log(obj1) console.log(obj2) // {a: 1, b: 100, c: 4} // {b: 3, c: 4}
let obj1 = {a:1,b:2} let obj2 = {b:3,c:4} // 構(gòu)建一個(gè)新對象obj,值是obj1和obj2的集合 let obj = {...obj1,...obj2} obj.b = 100 console.log(obj1) console.log(obj2) // {a: 1, b: 2} // {b: 3, c: 4}
在第一段代碼中,執(zhí)行完Object.assign()時(shí),obj1已經(jīng)改變了,而且改變組合出來的obj時(shí),obj1還會再改變。實(shí)際上我只想組合出一個(gè)完全獨(dú)立的obj來,可以肆意改變它,而不影響原始數(shù)據(jù)(想一下純函數(shù)的實(shí)現(xiàn),以及Redux等)。
ArrayEmm,數(shù)組拷貝能想到哪些?
slice(),concat(),Array.from(),...
這里就不一個(gè)一個(gè)試了,先給出結(jié)論吧:
都只能實(shí)現(xiàn)一維數(shù)組的深拷貝
看個(gè)例子:
let arr1 = [1, 2], arr2 = [...arr1]; console.log(arr1); // [1, 2] console.log(arr2); // [1, 2] arr2[0] = 3; console.log(arr1); // [1, 2] console.log(arr2); // [3, 2] let arr3 = [1, 2, [3, 4]], arr4 = [...arr3]; console.log(arr3); // [1, 2, [3, 4]] console.log(arr4); // [1, 2, [3, 4]] arr4[2][1] = 5; console.log(arr3); // [1, 2, [3, 5]] console.log(arr4); // [1, 2, [3, 5]]
好吧,那js里到底有沒有不限條件的深拷貝方法呢?看下這個(gè):
let obj1 = {x:1, y:{z:1}}; let obj2 = JSON.parse(JSON.stringify(obj1)); console.log(obj2) // {x:1, y:{z:1}} // 改一下obj2,看看會不會影響obj1 obj2.y.z = 2; console.log(obj1) // {x:1, y:{z:1}} // 可以,obj1沒有受到obj2的影響
簡單粗暴吧?不過JSON.parse(JSON.stringify())也并不是萬能的,比如對象的屬性是undefined,function()時(shí):
let obj1 = { x: 1, y: undefined, z: () => console.log("lalala") }; let obj2 = JSON.parse(JSON.stringify(obj1)); console.log(obj2); // {x: 1} // 源對象的屬性y和z都丟失了,更別說深拷貝了
那數(shù)組呢?有沒有不限條件的深拷貝方法,哪怕有個(gè)類似的簡單粗暴的不完全體也行啊。
據(jù)了解只有一種方法,建一個(gè)空數(shù)組,然后一級一級遍歷原數(shù)組并填充到里面吧。
JavaScript有兩種數(shù)據(jù)類型:基本數(shù)據(jù)類型和引用數(shù)據(jù)類型;
基本數(shù)據(jù)類型:String,Number,Boolean,Undefined,Null,Symbol
引用數(shù)據(jù)類型:Object,Array,F(xiàn)unction
JavaScript不允許直接訪問內(nèi)存中的位置,因此我們操作的只是對象引用,而不是實(shí)際的對象;
深拷貝和淺拷貝是針對引用數(shù)據(jù)類型而言的;
JavaScript暫時(shí)還沒有實(shí)現(xiàn)多維數(shù)組/對象深拷貝的內(nèi)置方法(還是說,有,但是我不知道。。)
對了,補(bǔ)充一個(gè)知識點(diǎn):
有人說,我家=也可以實(shí)現(xiàn)一級深拷貝啊,你看:let arr1 = {a:1,b:2}; arr2 = arr1; arr2 = {a:0,b:1}; console.log(arr2); // {a:0, b:2} console.log(arr1); // {a:1, b:2}額。。不帶你這么玩的。。
arr2 = {a:0,b:1}; vs. arr2.a = 0;
這兩種操作可不是一碼事。。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/104034.html
摘要:深拷貝淺拷貝本文主要對深拷貝淺拷貝的解釋及實(shí)現(xiàn)做一下簡單記錄。之所以會有深拷貝與淺拷貝之分,是因?yàn)椴煌瑪?shù)據(jù)類型的數(shù)據(jù)在內(nèi)存中的存儲區(qū)域不一樣。但注意,只能做一層屬性的淺拷貝。 深拷貝VS淺拷貝 本文主要對深拷貝&淺拷貝的解釋及實(shí)現(xiàn)做一下簡單記錄。原文鏈接,歡迎star。 之所以會有深拷貝與淺拷貝之分,是因?yàn)椴煌瑪?shù)據(jù)類型的數(shù)據(jù)在內(nèi)存中的存儲區(qū)域不一樣。 堆和棧是計(jì)算機(jī)中劃分出來用來存儲的...
摘要:那么如何切斷和之間的關(guān)系呢,可以拷貝一份的數(shù)據(jù),根據(jù)拷貝的層級不同可以分為淺拷貝和深拷貝,淺拷貝就是只進(jìn)行一層拷貝,深拷貝就是無限層級拷貝。 深拷貝 vs 淺拷貝 深拷貝和淺拷貝都是針對的引用類型,JS中的變量類型分為值類型(基本類型)和引用類型;對值類型進(jìn)行復(fù)制操作會對值進(jìn)行一份拷貝,而對引用類型賦值,則會進(jìn)行地址的拷貝,最終兩個(gè)變量指向同一份數(shù)據(jù)。 // 基本類型 var a = ...
摘要:二淺拷貝與深拷貝深拷貝和淺拷貝是只針對和這樣的引用數(shù)據(jù)類型的。淺拷貝是按位拷貝對象,它會創(chuàng)建一個(gè)新對象,這個(gè)對象有著原始對象屬性值的一份精確拷貝。對于字符串?dāng)?shù)字及布爾值來說不是或者對象,會拷貝這些值到新的數(shù)組里。 一、數(shù)據(jù)類型 數(shù)據(jù)分為基本數(shù)據(jù)類型(String, Number, Boolean, Null, Undefined,Symbol)和對象數(shù)據(jù)類型。 基本數(shù)據(jù)類型的特點(diǎn):直...
閱讀 1245·2023-04-25 20:31
閱讀 3747·2021-10-14 09:42
閱讀 1526·2021-09-22 16:06
閱讀 2717·2021-09-10 10:50
閱讀 3558·2021-09-07 10:19
閱讀 1806·2019-08-30 15:53
閱讀 1194·2019-08-29 15:13
閱讀 2843·2019-08-29 13:20