深拷貝與淺拷貝的實(shí)現(xiàn)(一)

最近的學(xué)習(xí)中,仔細(xì)研究了下深拷貝和淺拷貝,下面就來簡單的總結(jié)下。

數(shù)據(jù)類型

首先我們了解下兩種數(shù)據(jù)類型

1、基本類型:像Number、String、Boolean等這種為基本類型

2、復(fù)雜類型:Object和Array

淺拷貝與深拷貝的概念

接著我們分別來了解下淺拷貝和深拷貝,深拷貝和淺拷貝是只針對(duì)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", "小雅"]


可以看出,對(duì)于對(duì)象或數(shù)組類型,當(dāng)我們將a賦值給b,然后更改b中的屬性,a也會(huì)隨著變化。

也就是說a和b指向了同一塊內(nèi)存,所以修改其中任意的值,另一個(gè)值都會(huì)隨之變化,這就是淺拷貝。

深拷貝

剛剛我們了解了什么是淺拷貝,那么相應(yīng)的,如果給b放到新的內(nèi)存中,將a的各個(gè)屬性都復(fù)制到新內(nèi)存里,就是深拷貝。

也就是說,當(dāng)b中的屬性有變化的時(shí)候,a內(nèi)的屬性不會(huì)發(fā)生變化。

淺拷貝

那么除了上面簡單的賦值引用,還有哪些方法使用了淺拷貝呢?

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()值得注意的地方。

深拷貝

那么下面我們就來說說復(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}}


通過上面的對(duì)比可以看出,當(dāng)使用extend()進(jìn)行深拷貝的時(shí)候,對(duì)象的所有屬性都添加到target中了。

我們知道了extend()可以進(jìn)行深拷貝,那么extend()是如何實(shí)現(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)于深拷貝的部分,取第一個(gè)參數(shù),如果是boolean類型的,就賦值給deep,下面如果deep為true(也就是進(jìn)行深拷貝),就遞歸調(diào)用extend(),這樣就將對(duì)象的所有屬性都添加到了target中實(shí)現(xiàn)了深拷貝。

JSON.parse()和JSON.stringify()

上面的jQuery源碼是否讓你眼花繚亂?有沒有什么辦法無腦實(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并沒有改變?cè)嫉膖arget,繼承的屬性也沒有丟失,因此實(shí)現(xiàn)了基本的深拷貝。

但是用JSON.parse()和JSON.stringify()會(huì)有一個(gè)問題。

JSON.parse()和JSON.stringify()能正確處理的對(duì)象只有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這個(gè)屬性由于是函數(shù)類型,使用JSON.parse()和JSON.stringify()后丟失了。

因此JSON.parse()和JSON.stringify()還是需要謹(jǐn)慎使用。

下篇文章我會(huì)繼續(xù)為大家說明深拷貝的各種實(shí)現(xiàn)。

未完待續(xù)……

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容