Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
383 views
in Technique[技术] by (71.8m points)

javascript - 如何正确克隆JavaScript对象?(How do I correctly clone a JavaScript object?)

I have an object, x .

(我有一个对象x 。)

I'd like to copy it as object y , such that changes to y do not modify x .

(我想将其复制为对象y ,以使对y更改不会修改x 。)

I realized that copying objects derived from built-in JavaScript objects will result in extra, unwanted properties.

(我意识到,复制从内置JavaScript对象派生的对象将导致额外的不需要的属性。)

This isn't a problem, since I'm copying one of my own, literal-constructed objects.

(这不是问题,因为我正在复制自己的文字构造对象之一。)

How do I correctly clone a JavaScript object?

(如何正确克隆JavaScript对象?)

  ask by mindeavor translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

To do this for any object in JavaScript will not be simple or straightforward.

(在JavaScript中对任何对象执行此操作都不是简单或直接的。)

You will run into the problem of erroneously picking up attributes from the object's prototype that should be left in the prototype and not copied to the new instance.

(您将遇到错误地从对象的原型中选取应保留在原型中而不应复制到新实例的属性的问题。)

If, for instance, you are adding a clone method to Object.prototype , as some answers depict, you will need to explicitly skip that attribute.

(例如,如果要将clone方法添加到Object.prototype ,如一些答案所示,则需要显式跳过该属性。)

But what if there are other additional methods added to Object.prototype , or other intermediate prototypes, that you don't know about?

(但是,如果还有其他您不知道的其他方法添加到Object.prototype或其他中间原型,该怎么办?)

In that case, you will copy attributes you shouldn't, so you need to detect unforeseen, non-local attributes with the hasOwnProperty method.

(在这种情况下,您将复制不应复制的属性,因此需要使用hasOwnProperty方法检测无法预见的非本地属性。)

In addition to non-enumerable attributes, you'll encounter a tougher problem when you try to copy objects that have hidden properties.

(除了不可枚举的属性外,当您尝试复制具有隐藏属性的对象时,还会遇到更棘手的问题。)

For example, prototype is a hidden property of a function.

(例如, prototype是函数的隐藏属性。)

Also, an object's prototype is referenced with the attribute __proto__ , which is also hidden, and will not be copied by a for/in loop iterating over the source object's attributes.

(同样,对象的原型使用属性__proto__引用,该属性也被隐藏,并且不会通过在源对象的属性上进行迭代的for / in循环进行复制。)

I think __proto__ might be specific to Firefox's JavaScript interpreter and it may be something different in other browsers, but you get the picture.

(我认为__proto__可能特定于Firefox的JavaScript解释器,并且在其他浏览器中可能有所不同,但是您可以理解。)

Not everything is enumerable.

(并不是所有的东西都可以列举。)

You can copy a hidden attribute if you know its name, but I don't know of any way to discover it automatically.

(如果知道隐藏属性的名称,则可以复制它,但是我不知道有什么方法可以自动发现它。)

Yet another snag in the quest for an elegant solution is the problem of setting up the prototype inheritance correctly.

(寻求优雅解决方案的另一个障碍是正确设置原型继承的问题。)

If your source object's prototype is Object , then simply creating a new general object with {} will work, but if the source's prototype is some descendant of Object , then you are going to be missing the additional members from that prototype which you skipped using the hasOwnProperty filter, or which were in the prototype, but weren't enumerable in the first place.

(如果源对象的原型为Object ,然后简单地创建一个新的通用对象{}的工作,但如果源的原型是一些后裔Object ,那么你将要丢失从原型您使用跳过了其他成员hasOwnProperty过滤器,或在原型中,但首先不能枚举。)

One solution might be to call the source object's constructor property to get the initial copy object and then copy over the attributes, but then you still will not get non-enumerable attributes.

(一种解决方案可能是调用源对象的constructor属性来获取初始复制对象,然后在属性上进行复制,但是这样您仍然不会获得不可枚举的属性。)

For example, a Date object stores its data as a hidden member:

(例如, 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() + "
d2 = " + d2.toString());
}, 5000);

The date string for d1 will be 5 seconds behind that of d2 .

(d1的日期字符串将比d2的日期字符串晚5秒。)

A way to make one Date the same as another is by calling the setTime method, but that is specific to the Date class.

(使一个Date与另一个Date相同的方法是调用setTime方法,但这特定于Date类。)

I don't think there is a bullet-proof general solution to this problem, though I would be happy to be wrong!

(尽管我很乐意弄错,但我认为没有解决该问题的通用解决方案!)

When I had to implement general deep copying I ended up compromising by assuming that I would only need to copy a plain Object , Array , Date , String , Number , or Boolean .

(当我不得不实施常规的深层复制时,以假定只复制一个简单的ObjectArrayDateStringNumberBoolean来折衷。)

The last 3 types are immutable, so I could perform a shallow copy and not worry about it changing.

(后三种类型是不可变的,因此我可以执行浅表复制,而不必担心它会更改。)

I further assumed that any elements contained in Object or Array would also be one of the 6 simple types in that list.

(我进一步假设ObjectArray包含的任何元素也将是该列表中6个简单类型之一。)

This can be accomplished with code like the following:

(这可以通过以下代码来完成:)

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.");
}

The above function will work adequately for the 6 simple types I mentioned, as long as the data in the objects and arrays form a tree structure.

(只要对象和数组中的数据形成树形结构,上述功能就可以对我提到的6种简单类型充分发挥作用。)

That is, there isn't more than one reference to the same data in the object.

(也就是说,对象中对同一数据的引用不止一个。)

For example:

(例如:)

// 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;

It will not be able to handle any JavaScript object, but it may be sufficient for many purposes as long as you don't assume that it will just work for anything you throw at it.

(它将无法处理任何JavaScript对象,但只要您不认为它会对您扔给它的任何东西起作用,它就可以满足许多目的。)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...