淺談JavaScript淺拷貝和深拷貝
網(wǎng)上關(guān)于這個(gè)話題,討論有很多了,根據(jù)各路情況我自己整理了一下,最后還是能接近完美的實(shí)現(xiàn)深拷貝,歡迎大家討論。
javascript
中的對(duì)象是引用類型,在復(fù)制對(duì)象的時(shí)候就要考慮是用淺拷貝還是用深拷貝。
一、直接賦值
對(duì)象是引用類型,如果直接賦值給另外一個(gè)對(duì)象,那么只是賦值一個(gè)引用,實(shí)際上兩個(gè)變量指向的同一個(gè)數(shù)據(jù)對(duì)象,如果其中一個(gè)對(duì)象的屬性變更,那么另外一個(gè)也會(huì)變更。
示例1,簡(jiǎn)單示例:
let human1 = { id: 1, name: "happy" }; human2 = human1; // 這里就是直接賦值 console.log(human1); // {id: 1, name: 'happy'} console.log(human2); // {id: 1, name: 'happy'} // 更改human1的名稱,human2的也會(huì)更改 human1.name = "life"; console.log(human1); // {id: 1, name: 'life'} console.log(human2); // {id: 1, name: 'life'}
示例2,對(duì)象作為參數(shù)傳遞也是傳遞引用:
let human1 = { id: 1, name: "happy" }; console.log(human1); // {id: 1, name: 'happy'} function foo(human) { // 這里更改了human對(duì)象的名稱 human.name = "life"; } foo(human1); // 傳遞對(duì)象是傳遞引用 console.log(human1); // {id: 1, name: 'life'}
二、淺拷貝
淺拷貝只是復(fù)制了對(duì)象的第一層,如果第一層的屬性值是對(duì)象,那么該屬性也只是復(fù)制了一個(gè)引用。
let object1 = { a: 1, b: { // b是對(duì)象 b1: 2 } }; object2 = Object.assign({}, object1); // 這里就是淺拷貝,其中的b對(duì)象只復(fù)制了引用 // a是常規(guī)類型,不會(huì)互相影響 object1.a = 10; console.log(object1.a); // 10 console.log(object2.a); // 1 // b是對(duì)象,會(huì)互相影響 object1.b.b1 = 20; console.log(object1.b.b1); // 20 console.log(object2.b.b1); // 20
如果要實(shí)現(xiàn)完整的復(fù)制,就要使用深拷貝。
三、深拷貝
森拷貝就是不光是一層要復(fù)制,里面的層如果是對(duì)象也要進(jìn)行復(fù)制。
1. JSON對(duì)象的方式
如果對(duì)象可以確認(rèn)是JSON
對(duì)象,那么可以用JSON
對(duì)象的方式。
沿用上面的例子:
let object1 = { a: 1, b: { // b是對(duì)象 b1: 2 } }; object2 = JSON.parse(JSON.stringify(object1)); // 深拷貝 // a是常規(guī)類型,不會(huì)互相影響 object1.a = 10; console.log(object1.a); // 10 console.log(object2.a); // 1 // b是對(duì)象,也不會(huì)互相影響 object1.b.b1 = 20; console.log(object1.b.b1); // 20 console.log(object2.b.b1); // 2
這邊深拷貝的原理其實(shí)就是先把對(duì)象轉(zhuǎn)成json
字符串,然后再轉(zhuǎn)成json
對(duì)象,中間轉(zhuǎn)成json
字符串后就和原來(lái)的對(duì)象沒有關(guān)系了。
這方法的優(yōu)點(diǎn):實(shí)現(xiàn)非常簡(jiǎn)單。
缺點(diǎn):
如果有屬性值是函數(shù)的話,那么無(wú)法進(jìn)行復(fù)制,數(shù)據(jù)會(huì)丟失。
另外原型對(duì)象無(wú)法進(jìn)行復(fù)制。
所以這種方式只適合對(duì)象確認(rèn)是一個(gè)純粹的json
數(shù)據(jù)。
2. 遞歸復(fù)制
因?yàn)樾枰粚右粚舆f進(jìn)復(fù)制,很容想到用遞歸的方式,參考如下實(shí)現(xiàn):
function deepCopy(source) { // 如果不是對(duì)象或者是null則直接返回 if (typeof source !== 'object' || source === null) { return source; } let target = {}; // 遍歷復(fù)制屬性 for (let k in source) { if (!source.hasOwnProperty(k)) { continue; } if (typeof source[k] === 'object') { // 如果是對(duì)象,則遞歸復(fù)制 target[k] = deepCopy(source[k]); continue; } let descriptor = Object.getOwnPropertyDescriptor(source, k); Object.defineProperty(target, k, descriptor); } return target; }
因?yàn)槭且粚右粚訌?fù)制,所以復(fù)制完成后,兩個(gè)對(duì)象不會(huì)互相影響,并且也可以支持方法。
let object1 = { a: 1, b: { // b是對(duì)象 b1: 2 }, f: function() { // f是方法 console.log(3); } }; object2 = deepCopy(object1); // 深拷貝,也可以復(fù)制函數(shù)了。 object1.f(); // 3 object2.f(); // 3 // b是對(duì)象,也不會(huì)互相影響 object1.b.b1 = 20; console.log(object1.b.b1); // 20 console.log(object2.b.b1); // 2
復(fù)制原型對(duì)象
但是這個(gè)方法還存在一個(gè)問(wèn)題,就是原型對(duì)象無(wú)法復(fù)制,稍微改進(jìn)一下:
// 把 let target = {}; 改成如下方式 // 保證原型也進(jìn)行了復(fù)制 let target = Object.create(Object.getPrototypeOf(source));
這樣就可以了,來(lái)個(gè)示例驗(yàn)證一下:
function Human() { this.id = 1; } Human.prototype.bar = function() { console.log("bar"); }; let human1 = new Human(); human2 = deepCopy(human1); console.log("human1", human1); console.log("human2", human2);
查看下兩個(gè)對(duì)象的原型:
深拷貝復(fù)制原型對(duì)象:
完美復(fù)制。
當(dāng)然這樣的方法也存在一個(gè)問(wèn)題,就是遞歸本身存在如果層次過(guò)深,容易造成棧溢出的問(wèn)題。但是在實(shí)務(wù)中也建議不要復(fù)制非常大的對(duì)象,應(yīng)該有另外好的解決方法。
到此這篇關(guān)于淺談JavaScript
淺拷貝和深拷貝的文章就介紹到這了,更多相關(guān)JavaScript
淺拷貝和深拷貝內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
參考文檔:
JS實(shí)現(xiàn)深拷貝:https://www.cnblogs.com/dobeco/p/11295316.html
Object.assign():https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Object.create():https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create
Object.getPrototypeOf():https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/getPrototypeOf
Object.defineProperty():https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
Object.getOwnPropertyDescriptor():https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor
hasOwnProperty():https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty
相關(guān)文章
Javascript中bind()方法綁定函數(shù)的使用與實(shí)現(xiàn)
這篇文章主要為大家介紹了Javascript中bind()方法綁定函數(shù)的使用與實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06判斷Spartacus?SSR的Transfer?State是否正常工作技巧
這篇文章主要為大家介紹了判斷Spartacus?SSR的Transfer?State是否正常工作技巧,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10微信小程序 實(shí)現(xiàn)拖拽事件監(jiān)聽實(shí)例詳解
這篇文章主要介紹了微信小程序 實(shí)現(xiàn)拖拽事件監(jiān)聽實(shí)例詳解的相關(guān)資料,在開發(fā)不少應(yīng)用或者軟件都要用到這樣的方法,這里就對(duì)微信小程序?qū)崿F(xiàn)該功能進(jìn)行介紹,需要的朋友可以參考下2016-11-11JavaScript+HTML實(shí)現(xiàn)學(xué)生信息管理系統(tǒng)
這篇文章主要介紹了JavaScript實(shí)現(xiàn)學(xué)生信息管理系統(tǒng),文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)js的小伙伴們有一定的幫助,需要的朋友可以參考下2021-04-04可拖動(dòng)窗口,附帶鼠標(biāo)控制漸變透明,開啟關(guān)閉功能
可拖動(dòng)窗口,附帶鼠標(biāo)控制漸變透明,開啟關(guān)閉功能...2006-06-06JS代理對(duì)象Proxy初體驗(yàn)簡(jiǎn)單的數(shù)據(jù)驅(qū)動(dòng)視圖
這篇文章主要為大家介紹了JS代理對(duì)象Proxy初體驗(yàn)簡(jiǎn)單的數(shù)據(jù)驅(qū)動(dòng)視圖,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08'2'>'10'==true?解析JS如何進(jìn)行隱式類型轉(zhuǎn)換
這篇文章主要為大家介紹了'2'>'10'==true?解析JS如何進(jìn)行隱式類型轉(zhuǎn)換示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09