在 JavaScript 中,實現深拷貝的方式有很多種,每種方式都有其優點和缺點。今天介紹一種原生 JavaScript 提供的structuredClone實現深拷貝。 下麵列舉一些常見的方式,以及它們的代碼示例和優缺點: 1. 使用 JSON.parse(JSON.stringify(obj)) ...
在 JavaScript 中,實現深拷貝的方式有很多種,每種方式都有其優點和缺點。今天介紹一種原生 JavaScript 提供的structuredClone
實現深拷貝。
下麵列舉一些常見的方式,以及它們的代碼示例和優缺點:
1. 使用 JSON.parse(JSON.stringify(obj))
代碼示例:
function deepClone(obj) { return JSON.parse(JSON.stringify(obj)); }
優點:簡單易行,對於大多數對象類型有效。
缺點:不能複製原型鏈,對於包含迴圈引用的對象可能出現問題。比如以下代碼:
const calendarEvent = { date: new Date() } const problematicCopy = JSON.parse(JSON.stringify(calendarEvent))
最終得到的 date 不是 Data 對象,而是字元串。
{ "date": "2024-03-02T03:43:35.890Z" }
這是因為JSON.stringify
只能處理基本的對象、數組。任何其他類型都沒有按預期處理。例如,日期轉換為字元串。Set/Map 只是轉換為{}
。
const kitchenSink = { set: new Set([1, 3, 3]), map: new Map([[1, 2]]), regex: /foo/, deep: { array: [ new File(someBlobData, 'file.txt') ] }, error: new Error('Hello!') } const veryProblematicCopy = JSON.parse(JSON.stringify(kitchenSink))
最終得到如下數據:
{ "set": {}, "map": {}, "regex": {}, "deep": { "array": [ {} ] }, "error": {}, }
2. 使用遞歸
代碼示例:
function deepClone(obj) { if (obj === null || typeof obj !== 'object') { return obj; } let clone = obj.constructor(); for (let attr in obj) { if (obj.hasOwnProperty(attr)) { clone[attr] = this.deepClone(obj[attr]); } } return clone; }
優點:對於任何類型的對象都有效,包括迴圈引用。
缺點:對於大型對象可能會消耗大量記憶體,並可能導致堆棧溢出。
3. 第三方庫,如 lodash 的 _.cloneDeep 方法
代碼示例:
const _ = require('lodash'); function deepClone(obj) { return _.cloneDeep(obj); }
優點:支持更多類型的對象和庫,例如,支持 Proxy 對象。
缺點:會引入依賴導致項目體積增大。
因為這個函數會導致 17.4kb 的依賴引入,如果只是引入 lodash 會更高。
4. 現代深拷貝 structuredClone
在現代瀏覽器中,可以使用 structuredClone
方法來實現深拷貝,它是一種更高效、更安全的深拷貝方式。
以下是一個示例代碼,演示如何使用 structuredClone
進行深拷貝:
const kitchenSink = { set: new Set([1, 3, 3]), map: new Map([[1, 2]]), regex: /foo/, deep: { array: [ new File(someBlobData, 'file.txt') ] }, error: new Error('Hello!') } kitchenSink.circular = kitchenSink const clonedSink = structuredClone(kitchenSink)
structuredClone
可以做到:
- 拷貝無限嵌套的對象和數組
- 拷貝迴圈引用
- 拷貝各種各樣的 JavaScript 類型,如
Date
、Set
、Map
、Error
、RegExp
、ArrayBuffer
、Blob
、File
、ImageData
等
哪些不能拷貝:
- 函數
- DOM 節點
- 屬性描述、
setter
和getter
- 對象原型鏈
所支持的完整列表:
Array
、ArrayBuffer
、Boolean
、DataView
、Date
、Error
類型(下麵具體列出的類型)、Map
、Object
,但僅限於普通對象、原始類型,除了symbol
(又名number
、string
、null
、undefined
、boolean
、BigInt
)、RegExp
、Set
、TypedArray
Error 類型:
Error
, EvalError
, RangeError
, ReferenceError
, SyntaxError
, TypeError
, URIError
Web/API 類型:
AudioData
, Blob
, CryptoKey
, DOMException
, DOMMatrix
, DOMMatrixReadOnly
, DOMPoint
, DomQuad
, DomRect
, File
, FileList
, FileSystemDirectoryHandle
, FileSystemFileHandle
, FileSystemHandle
, ImageBitmap
, ImageData
, RTCCertificate
, VideoFrame
值得慶幸的是 structuredClone
在所有主流瀏覽器中都受支持,也支持 Node.js 和 Deno。
結語
我們現在終於可以直接使用原生 JavaScript 中的structuredClone
能力實現深度拷貝對象。每種方式都有其優缺點,具體使用方式取決於你的需求和目標對象的類型。
更多內容請看:https://mybj123.com/20631.html