最近的學(xué)習(xí)中期丰,仔細(xì)研究了下深拷貝和淺拷貝,下面就來(lái)簡(jiǎn)單的總結(jié)下漠嵌。
數(shù)據(jù)類(lèi)型
首先我們了解下兩種數(shù)據(jù)類(lèi)型:
1咐汞、基本類(lèi)型:像Number、String儒鹿、Boolean等這種為基本類(lèi)型
2化撕、復(fù)雜類(lèi)型:Object和Array
淺拷貝與深拷貝的概念
接著我們分別來(lái)了解下淺拷貝和深拷貝,深拷貝和淺拷貝是只針對(duì)Object和Array這樣的復(fù)雜類(lèi)型的约炎。
淺拷貝:
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", "小雅"]
可以看出植阴,對(duì)于對(duì)象或數(shù)組類(lèi)型,當(dāng)我們將a賦值給b圾浅,然后更改b中的屬性掠手,a也會(huì)隨著變化。
也就是說(shuō)a和b指向了同一塊內(nèi)存狸捕,所以修改其中任意的值喷鸽,另一個(gè)值都會(huì)隨之變化,這就是淺拷貝灸拍。
深拷貝:
剛剛我們了解了什么是淺拷貝做祝,那么相應(yīng)的,如果給b放到新的內(nèi)存中鸡岗,將a的各個(gè)屬性都復(fù)制到新內(nèi)存里混槐,就是深拷貝。
也就是說(shuō)轩性,當(dāng)b中的屬性有變化的時(shí)候声登,a內(nèi)的屬性不會(huì)發(fā)生變化。
淺拷貝
那么除了上面簡(jiǎn)單的賦值引用,還有哪些方法使用了淺拷貝呢悯嗓?
Object.assign()
在MDN上介紹Object.assign():”O(jiān)bject.assign() 方法用于將所有可枚舉的屬性的值從一個(gè)或多個(gè)源對(duì)象復(fù)制到目標(biāo)對(duì)象件舵。它將返回目標(biāo)對(duì)象「”
復(fù)制一個(gè)對(duì)象
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()拷貝的只是屬性值,假如源對(duì)象的屬性值是一個(gè)指向?qū)ο蟮囊枚砣希仓豢截惸莻€(gè)引用值个少。所以O(shè)bject.assign()只能用于淺拷貝或是合并對(duì)象。這是Object.assign()值得注意的地方眯杏。
深拷貝
那么下面我們就來(lái)說(shuō)說(shuō)復(fù)雜的深拷貝夜焦。
jQuery.extend()
說(shuō)到深拷貝,第一想到的就是jQuery.extend()方法岂贩,下面我們簡(jiǎn)單看下jQuery.extend()的使用茫经。
jQuery.extend( [deep ], target, object1 [, objectN ] ),其中deep為Boolean類(lèi)型萎津,如果是true卸伞,則進(jìn)行深拷貝。
我們還是用上面的數(shù)據(jù)來(lái)看下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}}
通過(guò)上面的對(duì)比可以看出荤傲,當(dāng)使用extend()進(jìn)行深拷貝的時(shí)候,對(duì)象的所有屬性都添加到target中了颈渊。
我們知道了extend()可以進(jìn)行深拷貝遂黍,那么extend()是如何實(shí)現(xiàn)深拷貝的呢体箕?
先來(lái)看下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)于深拷貝的部分脚作,取第一個(gè)參數(shù)测萎,如果是boolean類(lèi)型的丙者,就賦值給deep,下面如果deep為true(也就是進(jìn)行深拷貝)摩骨,就遞歸調(diào)用extend()奏赘,這樣就將對(duì)象的所有屬性都添加到了target中實(shí)現(xiàn)了深拷貝胡野。
JSON.parse()和JSON.stringify()
上面的jQuery源碼是否讓你眼花繚亂竹揍?有沒(méi)有什么辦法無(wú)腦實(shí)現(xiàn)深拷貝呢敬飒?JSON.parse()和JSON.stringify()給了我們一個(gè)基本的解決辦法。
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并沒(méi)有改變?cè)嫉膖arget鬼佣,繼承的屬性也沒(méi)有丟失驶拱,因此實(shí)現(xiàn)了基本的深拷貝霜浴。
但是用JSON.parse()和JSON.stringify()會(huì)有一個(gè)問(wèn)題晶衷。
JSON.parse()和JSON.stringify()能正確處理的對(duì)象只有Number、String、Array等能夠被json表示的數(shù)據(jù)結(jié)構(gòu)晌纫,因此函數(shù)這種不能被json表示的類(lèi)型將不能被正確處理税迷。
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這個(gè)屬性由于是函數(shù)類(lèi)型锹漱,使用JSON.parse()和JSON.stringify()后丟失了箭养。
因此JSON.parse()和JSON.stringify()還是需要謹(jǐn)慎使用。
下篇文章我會(huì)繼續(xù)為大家說(shuō)明深拷貝的各種實(shí)現(xiàn)哥牍。
未完待續(xù)……