最近的學(xué)習(xí)中驰凛,仔細(xì)研究了下深拷貝和淺拷貝胸懈,下面就來簡單的總結(jié)下。
數(shù)據(jù)類型
首先我們了解下兩種數(shù)據(jù)類型:
1恰响、基本類型:像Number趣钱、String、Boolean等這種為基本類型
2胚宦、復(fù)雜類型:Object和Array
淺拷貝與深拷貝的概念
接著我們分別來了解下淺拷貝和深拷貝首有,深拷貝和淺拷貝是只針對Object和Array這樣的復(fù)雜類型的。
淺拷貝:
var a = {
myname: 'yana'
};
var b = a;
b.myname = '小雅';
console.log(b.myname); // 小雅
console.log(a.myname); // 小雅
var a = ['myname', 'yana'];
var b = a;
b[1] = '小雅';
console.log(a); // ["myname", "小雅"]
console.log(b); // ["myname", "小雅"]
可以看出枢劝,對于對象或數(shù)組類型井联,當(dāng)我們將a賦值給b,然后更改b中的屬性您旁,a也會隨著變化烙常。也就是說a和b指向了同一塊內(nèi)存,所以修改其中任意的值鹤盒,另一個值都會隨之變化蚕脏,這就是淺拷貝。
深拷貝:
剛剛我們了解了什么是淺拷貝侦锯,那么相應(yīng)的驼鞭,如果給b放到新的內(nèi)存中,將a的各個屬性都復(fù)制到新內(nèi)存里尺碰,就是深拷貝挣棕。
也就是說,當(dāng)b中的屬性有變化的時候亲桥,a內(nèi)的屬性不會發(fā)生變化洛心。
淺拷貝
那么除了上面簡單的賦值引用,還有哪些方法使用了淺拷貝呢两曼?
Object.assign()
在MDN上介紹Object.assign():”O(jiān)bject.assign() 方法用于將所有可枚舉的屬性的值從一個或多個源對象復(fù)制到目標(biāo)對象皂甘。它將返回目標(biāo)對象玻驻〉看眨”
復(fù)制一個對象
var target = {a: 1, b: 1};
var copy1 = {a: 2, b: 2, c: {ca: 21, cb: 22, cc: 23}};
var copy2 = {c: {ca: 31, cb: 32, cd: 34}};
var result = Object.assign(target, copy1, copy2);
console.log(target); // {a: 2, b: 2, c: {ca: 31, cb: 32, cc: 33}}
console.log(target === result); // true
可以看到偿枕,Object.assign()拷貝的只是屬性值,假如源對象的屬性值是一個指向?qū)ο蟮囊没П瑁仓豢截惸莻€引用值渐夸。所以O(shè)bject.assign()只能用于淺拷貝或是合并對象。這是Object.assign()值得注意的地方渔欢。
深拷貝
那么下面我們就來說說復(fù)雜的深拷貝墓塌。
jQuery.extend()
說到深拷貝,第一想到的就是jQuery.extend()方法奥额,下面我們簡單看下jQuery.extend()的使用苫幢。
jQuery.extend( [deep ], target, object1 [, objectN ] ),其中deep為Boolean類型垫挨,如果是true韩肝,則進(jìn)行深拷貝。
我們還是用上面的數(shù)據(jù)來看下extend()方法九榔。
var target = {a: 1, b: 1};
var copy1 = {a: 2, b: 2, c: {ca: 21, cb: 22, cc: 23}};
var copy2 = {c: {ca: 31, cb: 32, cd: 34}};
var result = $.extend(true, target, copy1, copy2); // 進(jìn)行深拷貝
console.log(target); // {a: 2, b: 2, c: {ca: 31, cb: 32, cc: 23, cd: 34}}
var target = {a: 1, b: 1};
var copy1 = {a: 2, b: 2, c: {ca: 21, cb: 22, cc: 23}};
var copy2 = {c: {ca: 31, cb: 32, cd: 34}};
var result = $.extend(target, copy1, copy2); // 不進(jìn)行深拷貝
console.log(target); // {a: 1, b: 1, c: {ca: 31, cb: 32, cd:34}}
通過上面的對比可以看出哀峻,當(dāng)使用extend()進(jìn)行深拷貝的時候,對象的所有屬性都添加到target中了哲泊。
我們知道了extend()可以進(jìn)行深拷貝剩蟀,那么extend()是如何實現(xiàn)深拷貝的呢?
先來看下jQuery.extend()源碼
jQuery.extend = jQuery.fn.extend = function() {
var options, name, src, copy, copyIsArray, clone,
target = arguments[ 0 ] || {},
i = 1,
length = arguments.length,
deep = false;
// Handle a deep copy situation
if ( typeof target === "boolean" ) {
deep = target;
// Skip the boolean and the target
target = arguments[ i ] || {};
i++;
}
// Handle case when target is a string or something (possible in deep copy)
if ( typeof target !== "object" && !jQuery.isFunction( target ) ) {
target = {};
}
// Extend jQuery itself if only one argument is passed
if ( i === length ) {
target = this;
i--;
}
for ( ; i < length; i++ ) {
// Only deal with non-null/undefined values
if ( ( options = arguments[ i ] ) != null ) {
// Extend the base object
for ( name in options ) {
src = target[ name ];
copy = options[ name ];
// Prevent never-ending loop
if ( target === copy ) {
continue;
}
// Recurse if we're merging plain objects or arrays
if ( deep && copy && ( jQuery.isPlainObject( copy ) || ( copyIsArray = Array.isArray( copy ) ) ) ) {
if ( copyIsArray ) {
copyIsArray = false;
clone = src && Array.isArray( src ) ? src : [];
} else {
clone = src && jQuery.isPlainObject( src ) ? src : {};
}
// Never move original objects, clone them
target[ name ] = jQuery.extend( deep, clone, copy );
// Don't bring in undefined values
} else if ( copy !== undefined ) {
target[ name ] = copy;
}
}
}
}
// Return the modified object
return target;
};
主要看下關(guān)于深拷貝的部分切威,取第一個參數(shù)育特,如果是boolean類型的,就賦值給deep先朦,下面如果deep為true(也就是進(jìn)行深拷貝)且预,就遞歸調(diào)用extend(),這樣就將對象的所有屬性都添加到了target中實現(xiàn)了深拷貝烙无。
JSON.parse()和JSON.stringify()
上面的jQuery源碼是否讓你眼花繚亂锋谐?有沒有什么辦法無腦實現(xiàn)深拷貝呢?JSON.parse()和JSON.stringify()給了我們一個基本的解決辦法截酷。
var target = {a: 1, b: 1, c: {ca: 11, cb: 12, cc: 13}};
var targetCopy = JSON.parse(JSON.stringify(target));
targetCopy.a = 2;
targetCopy.c.ca = 21;
console.log(target); // {a: 1, b: 1, c: {ca: 11, cb: 12, cc: 13}}
console.log(targetCopy); // {a: 2, b: 1, c: {ca: 21, cb: 12, cc: 13}}
console.log(target === targetCopy); // false
可以看到改變targetCopy并沒有改變原始的target涮拗,繼承的屬性也沒有丟失,因此實現(xiàn)了基本的深拷貝迂苛。
但是用JSON.parse()和JSON.stringify()會有一個問題三热。
JSON.parse()和JSON.stringify()能正確處理的對象只有Number、String三幻、Array等能夠被json表示的數(shù)據(jù)結(jié)構(gòu)就漾,因此函數(shù)這種不能被json表示的類型將不能被正確處理。
var target = {
a: 1,
b: 2,
hello: function() {
console.log("Hello, world!");
}
};
var copy = JSON.parse(JSON.stringify(target));
console.log(copy); // {a: 1, b: 2}
上面的例子可以看出念搬,hello這個屬性由于是函數(shù)類型抑堡,使用JSON.parse()和JSON.stringify()后丟失了摆出。
因此JSON.parse()和JSON.stringify()還是需要謹(jǐn)慎使用。
轉(zhuǎn)載自AlloyTeam:http://www.alloyteam.com/2017/08/12978/