克隆一个JavaScript对象?

16

可能是重复问题:
如何克隆js对象?

这是使用对象字面量符号而不是函数创建JavaScript对象的另一种方式:

user = {
  name: "Foo",
  email: "bar@baz.com"
}

这个对象是否可以被克隆或者它是一个单例模式?


3
JavaScript对象 != JSON对象。您提供的示例是JavaScript对象字面量;我已相应地更新了您的帖子。 - Matt Ball
1
最短的答案:是的和是的。;) - Marcel Korpel
@DaggNabbit,你的代码存在问题,无法达到预期目的。请查看我的两条评论,其中一条是你指向下面的评论。创建的对象引用了同一个对象,所以这不是一个克隆。如果我们想要做你的方法正在做的事情,那么在JS中很容易实现,不需要任何技巧来进行克隆。 - Om Shankar
@OmShankar,请停止试图为自己辩解。代码显然已经达到了它的目的,因为它帮助了提问者。如果你所说的“没有任何技巧”是指Object.create,请注意这些帖子的日期。除非你认为“深拷贝”是“克隆”的唯一可接受定义,否则你的其余评论完全没有意义,这是荒谬的。 - Dagg Nabbit
它不会改变克隆的自己属性。这就是为什么我强调了自己。自己,即hasOwnProperty。jQuery的clone显然是以cloneNode命名的,它是一个DOM函数,与JavaScript或原型语言中“克隆”的通常定义几乎没有关系(DOM内容是与语言无关的)。 - Dagg Nabbit
显示剩余8条评论
5个回答

29

试一试这个:

var clone = (function(){ 
  return function (obj) { Clone.prototype=obj; return new Clone() };
  function Clone(){}
}());

下面是代码的具体解释。

  • Clone 是一个虚构的构造函数。
  • 我们将想要克隆的对象分配给 Clone 构造函数的原型。
  • 使用 'new' 调用 Clone,因此构造出的对象具有原始对象作为其构造函数的原型,也就是(非标准的)__proto__

克隆对象将共享原始对象的所有属性,不会复制任何内容。如果为克隆对象的属性分配了新值,则不会影响原始对象。不需要篡改内置函数。

请注意,新创建的对象的属性将引用与克隆对象的同名属性相同的对象。将新值分配给克隆的属性不会影响原始对象,但是对克隆的对象属性进行值分配会影响。


在Chrome或Firebug控制台中尝试:

var user = {
  name: "Foo",
  email: "bar@baz.com"
}

var clonedUser = clone(user);

console.dir(clonedUser);

你可以在这里找到关于此克隆技术的详细解释。


2
user={name:"",email:"",obj:{a:"A"}}; clonedUser=clone(user); clonedUser.obj.a="B"; 你会发现user.obj.a == "B" - vol7ron
@DaggNabbit,绝对错了!创建一个对象:var abc = {a: 1, b: 2},然后尝试使用你的方法var def = clone(abc);注意改变abc.a也会改变def.a,哈哈。这怎么算是克隆?只有在它们指向同一引用时才会发生这种情况。 - Om Shankar
@DaggNabbit,另外,你的代码有问题。就像IIF中的function Clone(){}一样,它被提升了,所以把它放在return语句后面是令人困惑的。 - Om Shankar
@OmShankar,我在我的答案中已经涵盖了你的abc.a点。函数提升是JavaScript的一部分,不会产生歧义。我不确定为什么这让你感到困惑。 - Dagg Nabbit
@DaggNabbit,是的,这是一个部分。但是在主逻辑之后放置函数定义会让人感到困惑。困惑并不一定意味着不能理解。有意让人感到困惑的东西也可以是可理解的。 - Om Shankar
显示剩余13条评论

11
您可以使用JSON对象(现代浏览器已有此功能):
var user = {name: "Foo", email: "bar@baz.com" } 
var user2 = JSON.parse(JSON.stringify(user))

user2.name = "Bar";
alert(user.name + " " + user2.name); // Foo Bar

jsfiddle 中查看。


编辑

如果你需要在旧浏览器中运行此代码,请参见http://www.json.org/js.html


1
嗯...不会给你点踩,但我觉得这很愚蠢。首先,如果你要使用JSON,在你的页面中你将需要包含json2.js,因为仍有一些现代浏览器没有内置JSON,并且在相当长的一段时间内还将存在这样的浏览器。 要么编写或借用扩展实现,要么包含一个具有扩展实现的库。 - Sky Sanders
我只是提供另一种方法来做这件事。如果一个网站已经有json2.js,它就可以工作。 - Topera
1
如果一个对象同时具有属性方法,那么我的朋友,请注意,在使用这种JSON风格时,方法不会被克隆的 - Om Shankar
6
其实这个想法并不傻。比如说,如果你正在为一个嵌入式系统创建应用程序,你总是知道它将在哪种浏览器上运行;而且你知道它是一个数据对象(因此你不关心方法是否被克隆),那么这种方法就很容易、干净和安全。虽然这种方式并不适用于所有应用程序,但对我来说非常完美!谢谢! - user435779

4

我喜欢使用这个:

if (typeof Object.create !== 'function') {
    Object.create = function (o) {
        var F = function () {};
        F.prototype = o;
    return new F();
    };
}

那么我想要克隆的任何对象都可以这样做:

user = {
    name: "Foo",
    email: "bar@baz.com"
};
var user2 = Object.create(user);

正如《JavaScript语言精粹》所示(或类似)


1
你不应该这样做;请参考Kangax的回答 - Marcel Korpel
2
@Marcel 感谢您指出这一点。我会保留我的答案,并推荐Kangax的解释作为一个好的阅读材料。 - Chris J
@Marcel/Chris +1;Kangax的回答只是create不会在所有地方都接受两个属性。我的感觉是,如果每个人都使用这种方法,那么它将迫使供应商遵守标准。我最初没有使用FF,因为它不能正确显示所有内容,现在网络变得更加智能化,FF已经进行了修改以在quirksmode中更好地工作。如果程序员遵守标准并推动接受它们的浏览器,情况也将如此。 - vol7ron
如果JavaScript能够提供一种简单的方式来克隆对象,那就太好了。 - never_had_a_name
我最喜欢那个。 - Mike Thornley

2

大多数JavaScript框架都对对象克隆有很好的支持。

var a= {'key':'value'};
var b= jQuery.extend( true, {}, a );

同意:underscorejs 也有一个克隆方法 http://underscorejs.org/#clone - dreftymac

0
Object.prototype.clone = function clone(obj) {
                           obj = obj || this;
                           var new_obj  = {};

                           for( var p in obj ) {
                             if ( obj.hasOwnProperty(p) ) {
                               if( obj[p] !== null && typeof(obj[p]) === "object" ) {
                                 new_obj[p] = clone( obj[p] );
                               }
                               else {
                                 new_obj[p] = obj[p];
                               }
                             }
                           }

                           return new_obj;
                         };


/* Example */
var foo = {
     name:  "Foo"
   , email: "bar@baz.com"
   , obj:   {a:"A",b:"B"}
};

var bar   = foo.clone();
bar.name  = "Bar";
bar.obj.b = "C";


// foo and bar should have a different 'name'
// foo and bar should retain the same email
// foo and bar should have different values for <foo/bar>['obj']['b']
// foo and bar should have the same values for <foo/bar>['obj']['a']
console.dir(foo);
console.dir(bar);

3
不不不,千万不要动Object.prototype,否则会把一切都搞砸的哦 :P - Dagg Nabbit
2
除非你是使用JQuery的机器人之一,否则这种说法是不正确的。否则就不要乱动什么,也不要忘记如何做任何事情。 - vol7ron
2
不要生气。我通常避免使用jQuery。但有两件事情...1,这不是一个克隆,而是一份复制。名称“克隆”与此类技术相关:http://oranlooney.com/functional-javascript/ - Dagg Nabbit
5
不要篡改内置对象,如Object和Array。这不是因为jQuery的原因,而是因为当使用in迭代对象时,有些开发者会忘记使用hasOwnProperty方法,结果可能会在对象中出现额外的属性,因为其他开发者在不知情的情况下修改了内置对象。 - Dagg Nabbit
2
@no: 你提出了一个值得记住的有效观点,但我认为这并不足以本质上说"不要乱动原型"。 - vol7ron
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接