如何克隆Clone一个JavaScript对象

原文 标签 IT工具网 javascript

原问题有两个:

这个问题从提问那会儿开始,经过了6年旷日持久的讨论,jQuery作者John Resig也顺便推销了一下jQuery.clone()。实际上jQuery.clone表现得确实不错,后来的underscorelodash也都提供了类似函数。而JSON本身也能简单的实现拷贝功能。

这里先给一个这四个方案的性能比较:object clone jquery vs underscore vs lodash

从性能测试结果来看,在实现deep clone的前提下,表现最好的是Lo-Dash。但有两个地方需要注意的是:

  1. 这四种方案拷贝出来的结果是不一样的,因此不能直接根据此性能结果来下结论
  2. underscore不提供deep clone方法。因为他们觉得无法提供一个完美的能广泛适用的deep clone。对此问题的讨论github issue

以下内容是扩展,真正的答案在最后。

JSON对象clone

克隆一个JSON对象比较简单:

JSON.parse(JSON.stringify(obj));

对于一个JavaScript对象,这种方法无法克隆两类属性:prototype和function。

由于JavaScript对象中有以上两类特殊的属性,想要实现一种广泛适用没有bug的克隆一个JavaScript对象的操作,比较困难。

因此,通常说的JavaScript Object Clone中通常存在两个比较棘手的问题:

  • function不是对象实例所对应的JSON的属性
  • deep clone无法完美处理Object.prototype

对于第一个问题,我们可以不使用JSON.parse(JSON.stringify(obj));的方式,而是用遍历属性的方式来解决。

第二个问题就比较麻烦,这里稍微说一下。

Prototype Chain实现

对于prototype chain机制,其实现也是很直白的。

如果你在chrome的Dev Tools里查看过JavaScript对象,你就会知道每个对象都有一个proto属性。该属性有一个名字,它就是prototype.constructor,显示为:__proto__: constructor。这个属性在Debug时很有用。

如下图所示: __proto__ chain

可以看到,__proto__属性中还有一个Object.prototype对象,所以它是一个嵌套定义。

如何克隆Object.prototype

目前为止这是一个无解问题,根本原因是ECMAScript在这方面还不够完备。

首先是你无法用for...in循环来访问它,因为它是一个隐藏属性,你只能指定属性名来访问它,但你又不知道浏览器是用什么属性名来实现它的。虽然目前主流浏览器对它的命名为__proto__

其次是你无法真正拷贝它,因为non-enumerable属性。具体请看:

结论

首先要明确,你究竟想拷贝一个对象的什么?只是数据?还是包括functions, prototype chain和constructor?

  • 如果只是想拷贝数据,JSON.parse(JSON.stringify(obj));就足够了
  • 如果还想拷贝对象中的function定义,那就用Lo-Dash的_.cloneDeep(obj)
  • 如果还想拷贝通过function.prototype.property方式添加的属性,请使用jQuery的$.extend(true, {}, obj);

示例:JSFiddle clone object

其他目的,请自行实现。

相关文章:

java遍历集合时移除元素,怎样避免ConcurrentModificationException异常抛出

检查iOS framework是否支持bitcode

如何开始Node.js的学习

javascript - 如何使其他指令在uib-tab元素中起作用

javascript - jQuery CSS最小高度

java如何用一行代码初始化一个ArrayList

如何定义一个JavaScript函数?

javascript - 如何从Node JS的客户端获取上传的文件?

javascript - 高库存-更改范围输入超出数据最大值

javascript - 按顺序对数组项执行函数