飞码网-免费源码博客分享网站

点击这里给我发消息

如何正确克隆JavaScript对象?|-Javascript教程

飞码网-免费源码博客分享网站 爱上飞码网—https://www.codefrees.com— 飞码网-matlab-python-C++ 爱上飞码网—https://www.codefrees.com— 飞码网-免费源码博客分享网站

问题:

我有一个对象x,我想把它复制成对象y,这样对y的改变就不会修改x。我意识到复制从内置JavaScript对象衍生的对象会导致额外的、不需要的属性。因为我复制的是我自己的一个文字构造的对象。

我如何正确地克隆一个JavaScript对象?

回答:

在JavaScript中对任何对象进行这样的操作都不会是简单或直接的。你会遇到这样的问题,那就是错误地从对象的原型中获取本应留在原型中而不是复制到新实例中的属性。例如,如果你要在Object.prototype中添加一个克隆方法,就像一些答案所描绘的那样,你需要显式地跳过这个属性。但是,如果在Object.prototype中添加了其他额外的方法,或者其他中间原型,而你又不知道呢?在这种情况下,你会复制不该复制的属性,所以你需要用hasOwnProperty方法来检测不可预见的非本地属性。

除了非数值属性,当你试图复制具有隐藏属性的对象时,你会遇到一个更棘手的问题。例如,prototype是一个函数的隐藏属性。另外,一个对象的prototype是用属性__proto__引用的,它也是隐藏的,不会被迭代源对象属性的for/in循环复制。我想 __proto__ 可能是 Firefox 的 JavaScript 解释器所特有的,在其他浏览器中可能会有所不同,但你明白了。并非所有的东西都是可枚举的。如果你知道一个隐藏属性的名字,你可以复制它,但我不知道有什么方法可以自动发现它。

然而,在寻求优雅解决方案的过程中,另一个障碍是正确设置原型继承的问题。如果你的源对象的原型是Object,那么简单地用{}创建一个新的通用对象就可以了,但是如果源对象的原型是Object的某个后裔,那么你就会遗漏该原型中的额外成员,这些成员是你用hasOwnProperty过滤器跳过的,或者是在原型中的,但一开始就不能枚举的。一个解决方案可能是调用源对象的构造函数属性来获取初始的复制对象,然后复制过来的属性,但这样你仍然不会得到不可枚举的属性。例如,一个Date对象将其数据存储为一个隐藏成员。
function clone(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    var copy = obj.constructor();
    for (var attr in obj) {
        if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
    }
    return copy;
}

var d1 = new Date();

/* Executes function after 5 seconds. */
setTimeout(function(){
    var d2 = clone(d1);
    alert("d1 = " + d1.toString() + "\nd2 = " + d2.toString());
}, 5000);
d1的日期字符串将比d2的日期字符串晚5秒。使一个Date与另一个Date相同的方法是调用setTime方法,但这是Date类特有的方法。我不认为有一个弹丸之地的通用解决方案来解决这个问题,尽管我很高兴自己是错的!

当我必须实现一般的深度复制时,我最终妥协了,假设我只需要复制一个普通的Object、Array、Date、String、Number或Boolean。最后3种类型是不可改变的,所以我可以执行浅层复制,而不用担心它的变化。我进一步假设Object或Array中包含的任何元素也将是该列表中的6种简单类型之一。这可以用下面这样的代码来实现。
function clone(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] = clone(obj[i]);
        }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
        copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");
}
只要对象和数组中的数据形成树形结构,上述函数对于我提到的6种简单类型就能充分地发挥作用。也就是说,对象中的同一数据没有一个以上的引用。比如说
// This would be cloneable:
var tree = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "right" : null,
    "data"  : 8
};

// This would kind-of work, but you would get 2 copies of the 
// inner node instead of 2 references to the same copy
var directedAcylicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
directedAcyclicGraph["right"] = directedAcyclicGraph["left"];

// Cloning this would cause a stack overflow due to infinite recursion:
var cyclicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
cyclicGraph["right"] = cyclicGraph;
它不能处理任何JavaScript对象,但只要你不认为它能处理你扔过来的任何东西就足够了。
飞码网-免费源码博客分享网站 爱上飞码网—https://www.codefrees.com— 飞码网-matlab-python-C++ 爱上飞码网—https://www.codefrees.com— 飞码网-免费源码博客分享网站
赞 ()
内容页底部广告位3
留言与评论(共有 0 条评论)
   
验证码: