摘要:的對象只是指向內存中某個位置的指針。所以在拷貝中的對象時,要根據實際情況做一些考慮。結論中最好的對象拷貝的算法,很大程度上取決于其使用環境,以及你需要拷貝的對象類型。
翻譯:瘋狂的技術宅
原文:https://smalldata.tech/blog/2...
本文首發微信公眾號:前端先鋒
歡迎關注,每天都給你推送新鮮的前端技術文章
在開始之前,我先普及一些基礎知識。Javascript 的對象只是指向內存中某個位置的指針。這些指針是可變的,也就是說,它們可以重新被賦值。所以僅僅復制這個指針,其結果是有兩個指針指向內存中的同一個地址。
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 } }深拷貝(有限制)
想要對一個對象進行深拷貝,一個可行的方法是先把對象序列化為字符串,然后再對它進行反序列化。
var obj = { a: 0, b: { c: 0 } }; var copy = JSON.parse(JSON.stringify(obj));
不幸的是,這個方法只在對象中包含可序列化值,同時沒有循環引用的情況下適用。常見的不能被序列化的就是日期對象 —— 盡管它顯示的是字符串化的 ISO 日期格式,但是 JSON.parse 只會把它解析成為一個字符串,而不是日期類型。
深拷貝 (限制較少)對于一些更復雜的場景,我們可以用 HTML5 提供的一個名為結構化克隆的新算法。不過,截至本文發布為止,有些內置類型仍然無法支持,但與 JSON.parse 相比較而言,它支持的類型要多的多:Date、RegExp、 Map、 Set、 Blob、 FileList、 ImageData、 sparse 和 typed Array。 它還維護了克隆對象的引用,這使它可以支持循環引用結構的拷貝,而這些在前面所說的序列化中是不支持的。
目前還沒有直接調用結構化克隆的方法,但是有些新的瀏覽器特性的底層用了這個算法。所以深拷貝對象可能需要依賴一系列的環境才能實現。
Via 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();
Via the 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; };
Via notification API: 當創建一個 notification 實例的時候,構造器為它相關的數據做了結構化克隆。需要注意的是,它會嘗試向用戶展示瀏覽器通知,但是除非它收到了用戶允許展示通知的請求,否則它什么都不會做。一旦用戶點擊同意的話,notification 會立刻被關閉。
const structuredClone = obj => { const n = new Notification("", {data: obj, silent: true}); n.onshow = n.close.bind(n); return n.data; };用 Node.js 進行深拷貝
Node.js 的 8.0.0 版本提供了一個 序列化 api 可以和結構化克隆相媲美. 不過這個 API 在本文發布的時候,還只是被標記為試驗性的:
const v8 = require("v8"); const buf = v8.serialize({a: "foo", b: new Date()}); const cloned = v8.deserialize(buf); cloned.b.getMonth();
在 8.0.0 版本以下比較穩定的方法,可以考慮用 lodash 的 cloneDeep函數,它的思想多少也基于結構化克隆算法。
結論Javascript 中最好的對象拷貝的算法,很大程度上取決于其使用環境,以及你需要拷貝的對象類型。雖然 lodash 是最安全的泛型深拷貝函數,但是如果你自己封裝的話,也許能夠獲得效率更高的實現方法,以下就是一個簡單的深拷貝,對 Date 日期對象也同樣適用:
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); }
我很期待可以隨便使用結構化克隆的那一天的到來,讓對象拷貝不再令人頭疼^_^
本文首發微信公眾號:前端先鋒 歡迎掃描二維碼關注公眾號,每天都給你推送新鮮的前端技術文章 歡迎繼續閱讀本專欄其它高贊文章:12個令人驚嘆的CSS實驗項目
必須要會的 50 個React 面試題
世界頂級公司的前端面試都問些什么
11 個最好的 JavaScript 動態效果庫
CSS Flexbox 可視化手冊
從設計者的角度看 React
過節很無聊?還是用 JavaScript 寫一個腦力小游戲吧!
CSS粘性定位是怎樣工作的
一步步教你用HTML5 SVG實現動畫效果
程序員30歲前月薪達不到30K,該何去何從
14個最好的 JavaScript 數據可視化庫
8 個給前端的頂級 VS Code 擴展插件
Node.js 多線程完全指南
把HTML轉成PDF的4個方案及實現
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/103636.html
摘要:所以,深拷貝是對對象以及對象的所有子對象進行拷貝實現方式就是遞歸調用淺拷貝對于深拷貝的對象,改變源對象不會對得到的對象有影響。 上一篇 JavaScript中的繼承 前言 文章開始之前,讓我們先思考一下這幾個問題: 為什么會有淺拷貝與深拷貝 什么是淺拷貝與深拷貝 如何實現淺拷貝與深拷貝 好了,問題出來了,那么下面就讓我們帶著這幾個問題去探究一下吧! 如果文章中有出現紕漏、錯誤之處...
摘要:原文地址基礎心法深淺拷貝歡迎。上面的代碼是最簡單的利用賦值操作符實現了一個淺拷貝,可以很清楚的看到,隨著和改變,和也隨著發生了變化。展開運算符結論實現的是對象第一層的深拷貝。 原文地址:JavaScript基礎心法——深淺拷貝 歡迎star。 如果有錯誤的地方歡迎指正。 淺拷貝和深拷貝都是對于JS中的引用類型而言的,淺拷貝就只是復制對象的引用,如果拷貝后的對象發生變化,原對象也會發生...
摘要:所以,深拷貝是對對象以及對象的所有子對象進行拷貝實現方式就是遞歸調用淺拷貝對于深拷貝的對象,改變源對象不會對得到的對象有影響。 為什么會有淺拷貝與深拷貝什么是淺拷貝與深拷貝如何實現淺拷貝與深拷貝好了,問題出來了,那么下面就讓我們帶著這幾個問題去探究一下吧! 如果文章中有出現紕漏、錯誤之處,還請看到的小伙伴多多指教,先行謝過 以下↓ 數據類型在開始了解 淺拷貝 與 深拷貝 之前,讓我們先...
摘要:專題系列第六篇,講解深淺拷貝的技巧和以及實現深淺拷貝的思路前言拷貝也是面試經典吶數組的淺拷貝如果是數組,我們可以利用數組的一些方法比如返回一個新數組的特性來實現拷貝。所以我們可以看出使用和是一種淺拷貝。 JavaScript 專題系列第六篇,講解深淺拷貝的技巧和以及實現深淺拷貝的思路 前言 拷貝也是面試經典吶! 數組的淺拷貝 如果是數組,我們可以利用數組的一些方法比如:slice、co...
摘要:它將返回目標對象。有些文章說是深拷貝,其實這是不正確的。深拷貝相比于淺拷貝速度較慢并且花銷較大。拷貝前后兩個對象互不影響。使用深拷貝的場景完全改變變量之后對沒有任何影響,這就是深拷貝的魔力。 一、賦值(Copy) 賦值是將某一數值或對象賦給某個變量的過程,分為: 1、基本數據類型:賦值,賦值之后兩個變量互不影響 2、引用數據類型:賦址,兩個變量具有相同的引用,指向同一個對象,相互之間有...
摘要:引用和值拷貝微信公眾號開發企業級產品全棧開發速成周末班首期班號正式開班,歡迎搶座作者黎躍春追時間的人簡介是推出的一個天挑戰。深拷貝與淺拷貝對比創建對象黎躍春淺拷貝深拷貝將對象轉換成字符串,打印時效果清晰。 Day14 - JavaScript 引用和值拷貝 (Node+Vue+微信公眾號開發)企業級產品全棧開發速成周末班首期班(10.28號正式開班,歡迎搶座) 作者:?黎躍春-追時間的...
閱讀 1813·2021-10-20 13:49
閱讀 1356·2019-08-30 15:52
閱讀 2862·2019-08-29 16:37
閱讀 1032·2019-08-29 10:55
閱讀 3063·2019-08-26 12:14
閱讀 1648·2019-08-23 17:06
閱讀 3235·2019-08-23 16:59
閱讀 2542·2019-08-23 15:42