物件的深拷貝是指其屬性與其拷貝的源物件的屬性不共用相同的參照(指向相同的底層值)的副本。
因此,當你更改源或副本時,可以確保不會導致其他物件也發生更改;也就是說,你不會無意中對源或副本造成意料之外的更改。
在深拷貝中,源和副本是完全獨立的。深拷貝與其源物件不共用參照,所以對深拷貝所做的任何更改都不會影響源物件。
JSON.stringify()
將該物件轉換為 JSON 字串,然後使用 JSON.parse()
將該字串轉換回(全新的)JavaScript 物件。前提:JavaScript 物件可以被序列化
序列化異常報錯
{ num: BigInt(1111111111) }
會丟擲 TypeError ("BigInt value can't be serialized in JSON")(BigInt 值不能 JSON 序列化).序列化需要注意的隱式轉換
undefined
、任意的函數以及 symbol
值,在序列化過程中會被忽略(出現在非陣列物件的屬性值中時)或者被轉換成 null
(出現在陣列中時)。函數、undefined
被單獨轉換時,會返回 undefined
,如 JSON.stringify(function(){})
or JSON.stringify(undefined)
.symbol
為屬性鍵的屬性都會被完全忽略掉,即便 replacer
引數中強制指定包含了它們。Date
日期,會呼叫 toJSON()
將其轉換為 string 字串(同 Date.toISOString())
NaN
和 Infinity
格式的數值及 null
都會被當做 null
Map/Set/WeakMap/WeakSet
,僅會序列化可列舉的屬性{}
範例
// 隱式轉換
JSON.stringify([new Number(1), new String("false"), new Boolean(false)]);
// '[1,"false",false]'
JSON.stringify({x: undefined, y: Object, z: Symbol("")});
// '{}'
JSON.stringify([undefined, Object, Symbol("")]);
// '[null,null,null]'
JSON.stringify({[Symbol("foo")]: "foo"});
// '{}'
JSON.stringify({[Symbol.for("foo")]: "foo"}, [Symbol.for("foo")]);
// '{}'
JSON.stringify(
{[Symbol.for("foo")]: "foo"},
function (k, v) {
if (typeof k === "symbol"){
return "a symbol";
}
}
);
// undefined
// 不可列舉的屬性預設會被忽略:
JSON.stringify(
Object.create(
null,
{
x: { value: 'x', enumerable: false },
y: { value: 'y', enumerable: true }
}
)
);
// "{"y":"y"}"
JSON.stringify(document.body) // '{}'
JSON.stringify(new Set([1, 2, 3])) // '{}'
JSON.stringify(new Map([['num', 2]])) // '{}'
window.structuredClone()
,使用結構化克隆演演算法將給定的值進行深拷貝。前提:可序列化的物件,且在瀏覽器環境和任何其他實現了 window 這樣全域性物件的 JavaScript 執行時的環境(structuredClone() 不是 JavaScript 語言本身的特性)
優點
補充:結構化克隆演演算法
postMessage()
或 IndexedDB
儲存物件時內部使用該演演算法。所以,也可間接的通過這2個方法實現深拷貝。補充:可轉移物件
ArrayBuffer
是一個擁有記憶體塊的可轉移物件。當此類緩衝區(buffer)線上程之間傳輸時,相關聯的記憶體資源將從原始的緩衝區分離出來,並且附加到新執行緒建立的緩衝區物件中。原始執行緒中的緩衝區物件不再可用,因為它不再擁有屬於自己的記憶體資源了。異常報錯
structuredClone(function fn() {})
structuredClone({s: Symbol(1)})
structuredClone(document.body)
隱式轉換
RegExp
物件的 lastIndex
欄位不會被保留setters
以及 getters
(以及其他類似後設資料的功能)同樣不會被複制。例如,如果一個物件用屬性描述符標記為 read-only
,它將會被複製為 read-write
,因為這是預設的情況下支援的js型別
支援的 Error型別
範例
// 迴圈參照
const original = { name: "MDN" };
original.itself = original;
// clone
const clone = structuredClone(original);
// 驗證
console.log(clone !== original) // true
console.log(clone.name === "MDN") // true
console.log(clone.itself === clone) // true
const get = { get foo() { return 'bar' } }
console.log(get.foo) // 'bar'
class MyClass {
foo = 'bar'
myMethod() { /* ... */ }
}
const myClass = new MyClass()
const cloned = structuredClone(myClass)
// { foo: 'bar' }
cloned instanceof myClass // false
比如 lodash 中的 cloneDeep()
方法
該方法會遞迴拷貝 value。
clone 方法參考自 結構化克隆演演算法 以及支援 arrays
、array buffers
、 booleans
、 date objects
、maps
、 numbers
, Object
物件, regexes
, sets
, strings
, symbols
, 以及 typed arrays
。 arguments
物件的可列舉屬性會拷貝為普通物件。 一些不可拷貝的物件,例如 error objects
、functions
, DOM nodes
, 以及 WeakMaps
會返回空物件。
參考:MDN
物件的淺拷貝是其屬性與拷貝源物件的屬性共用相同參照(指向相同的底層值)的副本。
因此,當你更改源或副本時,也可能導致其他物件也發生更改——也就是說,你可能會無意中對源或副本造成意料之外的更改。
在淺拷貝中,對源或副本的更改可能也會導致其他物件的更改(因為兩個物件共用相同的參照)。
在 js 中,所有標準的內建物件進行操作:...
展開語法、Array.prototype.concat()
、Array.prototype.slice()
、Array.from()
、Object.assign()
和 Object.create()
,建立的都是淺拷貝而不是深拷貝。
參考:MDN 淺拷貝
最後,感謝您閱讀這篇部落格!希望本文能夠為您提供有價值的資訊和啟示。
如果您對本文的內容有任何疑問或建議,請隨時在評論區留言,我會盡快回復您。