物件克隆

當你想要一個物件的完整副本(即物件屬性和這些屬性中的值等等)時,稱為深度克隆

Version >= 5.1

如果一個物件可以序列化為 JSON,那麼你可以使用 JSON.parseJSON.stringify 的組合建立它的深度克隆:

var existing = { a: 1, b: { c: 2 } };
var copy = JSON.parse(JSON.stringify(existing));
existing.b.c = 3; // copy.b.c will not change

請注意,JSON.stringify 會將 Date 物件轉換為 ISO 格式的字串表示形式,但 JSON.parse 不會將字串轉換回 Date

JavaScript 中沒有用於建立深度克隆的內建函式,並且通常不可能出於多種原因為每個物件建立深度克隆。例如,

  • 物件可以具有無法檢測到的不可列舉和隱藏屬性。
  • 物件 getter 和 setter 無法複製。
  • 物件可以具有迴圈結構。
  • 函式屬性可以取決於隱藏範圍中的狀態。

假設你有一個 nice 物件,其屬性僅包含原始值,日期,陣列或其他 nice 物件,則可以使用以下函式來建立深度克隆。它是一個遞迴函式,可以檢測具有迴圈結構的物件,並在這種情況下丟擲錯誤。

function deepClone(obj) {
    function clone(obj, traversedObjects) {
        var copy;
        // primitive types
        if(obj === null || typeof obj !== "object") {
            return obj;
        }

        // detect cycles
        for(var i = 0; i < traversedObjects.length; i++) {
            if(traversedObjects[i] === obj) {
                throw new Error("Cannot clone circular object.");
            }
        }

        // dates
        if(obj instanceof Date) {
            copy = new Date();
            copy.setTime(obj.getTime());
            return copy;
        }
        // arrays
        if(obj instanceof Array) {
            copy = [];
            for(var i = 0; i < obj.length; i++) {
                copy.push(clone(obj[i], traversedObjects.concat(obj)));
            }
            return copy;
        }
        // simple objects
        if(obj instanceof Object) {
            copy = {};
            for(var key in obj) {
                if(obj.hasOwnProperty(key)) {
                    copy[key] = clone(obj[key], traversedObjects.concat(obj));
                }
            }
            return copy;
        }
        throw new Error("Not a cloneable object.");
    }

    return clone(obj, []);
}