对象克隆

当你想要一个对象的完整副本(即对象属性和这些属性中的值等等)时,称为深度克隆

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, []);
}