摘要:原文在這篇文章中我們將會討論中對象拷貝的多種方式。因此,根據你的用法需要特別注意的對象拷貝。在擁有權限的情況下,通知立即關閉。在中深拷貝不幸的是,結構化拷貝算法目前僅適用于基于瀏覽器的應用。
原文:COPYING OBJECTS IN JAVASCRIPT
在這篇文章中我們將會討論 Javascript 中對象拷貝的多種方式。包括深拷貝和淺拷貝。
開始之前,先談一些基礎知識: Javascript 中的對象只是對內存中某個地址的引用。這些引用是可變的,即它們可以重新分配。因此,簡單制作一個引用的副本只會導致2個引用指向內存中相同的地址:
var foo = { a : "abc" } console.log(foo.a); // abc var bar = foo; console.log(bar.a); // abc foo.a = "yo foo"; console.log(foo.a); // yo foo console.log(bar.a); // yo foo bar.a = "whatup bar?"; console.log(foo.a); // whatup bar? console.log(bar.a); // whatup bar?
在上面的例子中可以看到,不管是foo還是bar都反映了它們對象上的變化。因此,根據你的用法需要特別注意Javascript的對象拷貝。
淺拷貝如果你的對象只有值類型的屬性,則可以用拓展語法或者
Object.assign(...)
var obj = { foo: "foo", bar: "bar" }; var copy = { ...obj }; // Object { foo: "foo", bar: "bar" }
var obj = { foo: "foo", bar: "bar" }; var copy = Object.assign({}, obj); // Object { foo: "foo", bar: "bar" }
注意,上述兩種方法都可以將屬性值從多個源對象復制到目標對象:
var obj1 = { foo: "foo" }; var obj2 = { bar: "bar" }; var copySpread = { ...obj1, ...obj2 }; // Object { foo: "foo", bar: "bar" } var copyAssign = Object.assign({}, obj1, obj2); // Object { foo: "foo", bar: "bar" }
上述方法的問題在于對于對象的屬性是對象的對象,只復制了引用地址,即它相當于執行 var bar = foo,與第一個代碼示例一樣:
var foo = { a: 0 , b: { c: 0 } }; var copy = { ...foo }; copy.a = 1; copy.b.c = 2; console.dir(foo); // { a: 0, b: { c: 2 } } console.dir(copy); // { a: 1, b: { c: 2 } }深拷貝(警告)
對于更加復雜的情況,可以使用較新的被稱為“結構化拷貝”的HTML5拷貝算法。不幸的是,它仍局限于某些內置類型,但它支持的類型比 JSON.parse 多得多:Date, RegExp, Map, Set, Blob, FileList, ImageData, 稀疏和類型化的Array。它還保留了拷貝數據中的引用,允許它支持不能與上述序列化方法一起使用的循環和遞歸結構。
目前沒有直接的方法來調用結構化拷貝算法,但是有一些較新的瀏覽器功能在引擎幫助下使用了這個算法。因此有幾個可以用來深拷貝的解決方法。
通過 MessageChannels:它的想法是利用通信功能使用的序列化算法。由于這個功能是基于事件的,因此生成的拷貝也是異步操作。
class StructuredCloner { constructor() { this.pendingClones_ = new Map(); this.nextKey_ = 0; const channel = new MessageChannel(); this.inPort_ = channel.port1; this.outPort_ = channel.port2; this.outPort_.onmessage = ({data: {key, value}}) => { const resolve = this.pendingClones_.get(key); resolve(value); this.pendingClones_.delete(key); }; this.outPort_.start(); } cloneAsync(value) { return new Promise(resolve => { const key = this.nextKey_++; this.pendingClones_.set(key, resolve); this.inPort_.postMessage({key, value}); }); } } const structuredCloneAsync = window.structuredCloneAsync = StructuredCloner.prototype.cloneAsync.bind(new StructuredCloner); const main = async () => { const original = { date: new Date(), number: Math.random() }; original.self = original; const clone = await structuredCloneAsync(original); // different objects: console.assert(original !== clone); console.assert(original.date !== clone.date); // cyclical: console.assert(original.self === original); console.assert(clone.self === clone); // equivalent values: console.assert(original.number === clone.number); console.assert(Number(original.date) === Number(clone.date)); console.log("Assertions complete."); }; main();
通過 history API: history.pushState() 和 history.replaceState() 都創建了它們第一個參數的結構化拷貝!注意當這個方法是異步的時候,操縱瀏覽器歷史記錄不再是一個快速操作,反復調用此方法可能會導致瀏覽器無響應。
const structuredClone = obj => { const oldState = history.state; history.replaceState(obj, null); const clonedObj = history.state; history.replaceState(oldState, null); return clonedObj; };
通過 notification API:在創建新通知時,構造函數會創建它關聯數據的結構化拷貝。注意它還會嘗試向用戶顯示瀏覽器通知,但是除非應用程序已經請求到了顯示通知的權限,否則這個操作將會失敗。在擁有權限的情況下,通知立即關閉。
const structuredClone = obj => { const n = new Notification("", {data: obj, silent: true}); n.onshow = n.close.bind(n); return n.data; };在 NODE.JS 中深拷貝
不幸的是,結構化拷貝算法目前僅適用于基于瀏覽器的應用。對于服務端,可以使用 lodash 的 cloneDeep 方法,該方法也是基于結構化拷貝方法。
結論總而言之,在Javascript中拷貝對象的最佳算法很大程度上取決于你要復制的對象的內容和類型。雖然 lodash 是最安全的通用深拷貝方法,但如果你自己動手,可能會獲得更高效的實現,以下是一個適用于日期的簡單深拷貝的例子:
function deepClone(obj) { var copy; // Handle the 3 simple types, and null or undefined if (null == obj || "object" != typeof obj) return obj; // Handle Date if (obj instanceof Date) { copy = new Date(); copy.setTime(obj.getTime()); return copy; } // Handle Array if (obj instanceof Array) { copy = []; for (var i = 0, len = obj.length; i < len; i++) { copy[i] = deepClone(obj[i]); } return copy; } // Handle Function if (obj instanceof Function) { copy = function() { return obj.apply(this, arguments); } return copy; } // Handle Object if (obj instanceof Object) { copy = {}; for (var attr in obj) { if (obj.hasOwnProperty(attr)) copy[attr] = deepClone(obj[attr]); } return copy; } throw new Error("Unable to copy obj as type isn"t supported " + obj.constructor.name); }
就個人而言,我期待能夠在任何地方使用結構化拷貝,最后把這個問題放一邊,快樂的拷貝:)
注:若有問題,請聯系我修改=。=
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/108659.html
摘要:所以,深拷貝是對對象以及對象的所有子對象進行拷貝實現方式就是遞歸調用淺拷貝對于深拷貝的對象,改變源對象不會對得到的對象有影響。 上一篇 JavaScript中的繼承 前言 文章開始之前,讓我們先思考一下這幾個問題: 為什么會有淺拷貝與深拷貝 什么是淺拷貝與深拷貝 如何實現淺拷貝與深拷貝 好了,問題出來了,那么下面就讓我們帶著這幾個問題去探究一下吧! 如果文章中有出現紕漏、錯誤之處...
摘要:所以,深拷貝是對對象以及對象的所有子對象進行拷貝實現方式就是遞歸調用淺拷貝對于深拷貝的對象,改變源對象不會對得到的對象有影響。 為什么會有淺拷貝與深拷貝什么是淺拷貝與深拷貝如何實現淺拷貝與深拷貝好了,問題出來了,那么下面就讓我們帶著這幾個問題去探究一下吧! 如果文章中有出現紕漏、錯誤之處,還請看到的小伙伴多多指教,先行謝過 以下↓ 數據類型在開始了解 淺拷貝 與 深拷貝 之前,讓我們先...
摘要:在中可以通過添加一個參數來實現遞歸,調用就可以實現一個深拷貝。利用序列化實現一個深拷貝 在JavaScript中,對于Object和Array這類引用類型值,當從一個變量向另一個變量復制引用類型值時,這個值的副本其實是一個指針,兩個變量指向同一個堆對象,改變其中一個變量,另一個也會受到影響。 這種拷貝分為兩種情況:拷貝引用和拷貝實例,也就是我們說的淺拷貝和深拷貝 淺拷貝(shallow...
摘要:實際上,是禁止這樣做的。傳值和傳址基本數據類型賦值基本數據類型的賦值是在內存中新開辟一段棧內存,然后再把再將值賦值到新的棧中。結果見輸出,可以看出來,無論是修改賦值得到的對象和淺拷貝得到的都會改變原始數據。 存儲問題:深拷貝和淺拷貝的主要區別:在內存中的存儲類型(堆和棧)不同堆:動態分配的內存,大小不定也不會自動釋放棧:自動分配的內存,由系統自動釋放數據類型: 基本數據類型: jav...
摘要:內存空間分為兩種,棧內存與堆內存棧是系統自動分配的內存空間,由系統自動釋放,堆則是動態分配的內存,大小不定不會自動釋放。 JavaScript的內存空間 在JavaScript中,每一個數據都需要一個內存空間。內存空間分為兩種,棧內存(stack)與堆內存(heap) 棧是系統自動分配的內存空間,由系統自動釋放,堆則是動態分配的內存,大小不定不會自動釋放。 基本數據類型 JavaScr...
摘要:原文地址基礎心法深淺拷貝歡迎。上面的代碼是最簡單的利用賦值操作符實現了一個淺拷貝,可以很清楚的看到,隨著和改變,和也隨著發生了變化。展開運算符結論實現的是對象第一層的深拷貝。 原文地址:JavaScript基礎心法——深淺拷貝 歡迎star。 如果有錯誤的地方歡迎指正。 淺拷貝和深拷貝都是對于JS中的引用類型而言的,淺拷貝就只是復制對象的引用,如果拷貝后的對象發生變化,原對象也會發生...
閱讀 1076·2021-10-14 09:42
閱讀 1369·2021-09-22 15:11
閱讀 3285·2019-08-30 15:56
閱讀 1243·2019-08-30 15:55
閱讀 3612·2019-08-30 15:55
閱讀 889·2019-08-30 15:44
閱讀 2028·2019-08-29 17:17
閱讀 2072·2019-08-29 15:37