搜索
首页web前端js教程JavaScript深度复制(deep clone)的实现方法_javascript技巧

在代码复用模式里面有一种叫做“复制属性模式”(copying properties pattern)。谈到代码复用的时候,很有可能想到的是代码的继承性(inheritance),但重要的是要记住其最终目标——我们要复用代码。继承性只是实现代码复用的一种手段,而不是唯一的方法。复制属性也是一种复用模式,它跟继承性是有所不同的。这种模式中,对象将从另外一个在对象中获取成员,其方法是仅需将其复制即可。用过jQuery的都知道,它有一个$.extend()方法,它的用途除了扩展第三方插件之外,还可以用来复制属性的。下面我们来看一个extend()函数的实现代码(注意这里的并不是jQuery的源码,只是一个简单的示例):

function extend(parent, child) {
var i;
//如果不传入第二参数child
//那么就创建一个新的对象
child = child || {}; 
//遍历parent对象的所有属性
//并且过滤原型上的属性
//然后将自身属性复制到child对象上
for(i in parent) {
if(parent.hasOwnProperty(i)) {
child[i] = parent[i];
}
}
//返回目标对象child
return child;
} 

上面的代码是一个简单的实现,它仅遍历父对象的成员并将其复制到子对象中去。下面我们用上面的extend()方法来测试一下:

var dad = {name: "Adam"};
var kid = extend(dad);
console.log(kid.name); //Adam 

我们发现,extend()方法已经可以正常工作了。但是有一个问题,上面给出的是一种所谓的浅复制(shallow clone)。在使用浅复制的时候,如果改变了子对象的属性,并且该属性恰好又是一个对象,那么这种操作也会修改父对象,单是很多情况这不是我们想要的结果。考虑下列情况:

var dad = {
counts: [1, 2, 3],
reads: {paper: true}
};
var kid = extend(dad) //调用extend()方法将dad的属性复制到kid上面
kid.counts.push(4); //把4追加到kid.counts数组里面
console.log(dad.counts); //[1, 2, 3, 4] 

通过上面的例子,我们会发现,修改了kid.counts属性以后(把元素4追加进去了),dad.counts也会受到影响。这是因为在使用浅复制的时候,由于对象是通过引用传递的,即kid.counts和dad.counts指向的是同一个数组(或者说在内存上他们指向同一个堆的地址)。

下面,让我们修改extend()函数以实现深度复制。我们需要做的事情就是检查父对象的每一个属性,如果该属性恰好是对象的话,那么就递归复制出该对象的属性。另外,还需要检测该对象是否为一个数组,这是因为数组的字面量创建方式和对象的字面量创建方式不一样,前者是[],后者是{}。检测数组可以使用Object.prototype.toString()方法进行检测,如果是数组的话,他会返回"[object Array]"。下面我们来看一下深度复制版本的extend()函数:

function extendDeep(parent, child) {
child = child || {};
for(var i in parent) {
if(parent.hasOwnProperty(i)) {
//检测当前属性是否为对象
if(typeof parent[i] === "object") {
//如果当前属性为对象,还要检测它是否为数组
//这是因为数组的字面量表示和对象的字面量表示不同
//前者是[],而后者是{}
child[i] = (Object.prototype.toString.call(parent[i]) === "[object Array]") ? [] : {};
//递归调用extend
extendDeep(parent[i], child[i]);
} else {
child[i] = parent[i];
}
}
}
return child;
} 

好了,深度复制的函数已经写好了,下面来测试一下看是否能够预期那样子工作,即是否可以实现深度复制:

var dad = {
counts: [1, 2, 3],
reads: {paper: true}
};
var kid = extendDeep(dad);
//修改kid的counts属性和reads属性
kid.counts.push(4);
kid.reads.paper = false;
console.log(kid.counts); //[1, 2, 3, 4]
console.log(kid.reads.paper); //false
console.log(dad.counts); //[1, 2, 3]
console.log(dad.reads.paper); //true 

通过上面例子,我们可以发现,即使修改了子对象的kid.counts和kid.reads,父对象的dad.counts和kid.reads并没有改变,因此我们的目的实现了。

下面来总结一下实现深复制的的基本思路:

1.检测当前属性是否为对象

2.因为数组是特殊的对象,所以,在属性为对象的前提下还需要检测它是否为数组。

3.如果是数组,则创建一个[]空数组,否则,创建一个{}空对象,并赋值给子对象的当前属性。然后,递归调用extendDeep函数。

上面例子使我们自己使用递归算法实现的一种深度复制方法。事实上,ES5新增的JSON对象提供的两个方法也可以实现深度复制,分别是JSON.stringify()和JSON.parse();前者用来将对象转成字符串,后者则把字符串转换成对象。下面我们使用该方法来实现一个深度复制的函数:

function extendDeep(parent, child) {
var i,
proxy;
proxy = JSON.stringify(parent); //把parent对象转换成字符串
proxy = JSON.parse(proxy) //把字符串转换成对象,这是parent的一个副本
child = child || {};
for(i in proxy) {
if(proxy.hasOwnProperty(i)) {
child[i] = proxy[i];
}
}
proxy = null; //因为proxy是中间对象,可以将它回收掉
return child;
} 

下面是测试例子:

var dad = {
counts: [1, 2, 3],
reads: {paper: true}
};
var kid = extendDeep(dad);
//修改kid的counts属性和reads属性
kid.counts.push(4);
kid.reads.paper = false;
console.log(kid.counts); //[1, 2, 3, 4]
console.log(kid.reads.paper); //false
console.log(dad.counts); //[1, 2, 3]
console.log(dad.reads.paper); //true 

测试发现,它也实现了深度复制。一般推荐使用后面这种方法,因为JSON.parse和JSON.stringify是内置函数,处理起来会比较快。另外,前面的那种方法使用了递归调用,我们都知道,递归是效率比较低的一种算法。

关于JavaScript深度复制(deep clone)的实现方法就给大家介绍这么多,希望对大家有所帮助!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
JavaScript数据类型:浏览器和nodejs之间是否有区别?JavaScript数据类型:浏览器和nodejs之间是否有区别?May 14, 2025 am 12:15 AM

JavaScript核心数据类型在浏览器和Node.js中一致,但处理方式和额外类型有所不同。1)全局对象在浏览器中为window,在Node.js中为global。2)Node.js独有Buffer对象,用于处理二进制数据。3)性能和时间处理在两者间也有差异,需根据环境调整代码。

JavaScript评论:使用//和 / * * / * / * /JavaScript评论:使用//和 / * * / * / * /May 13, 2025 pm 03:49 PM

JavaScriptusestwotypesofcomments:single-line(//)andmulti-line(//).1)Use//forquicknotesorsingle-lineexplanations.2)Use//forlongerexplanationsorcommentingoutblocksofcode.Commentsshouldexplainthe'why',notthe'what',andbeplacedabovetherelevantcodeforclari

Python vs. JavaScript:开发人员的比较分析Python vs. JavaScript:开发人员的比较分析May 09, 2025 am 12:22 AM

Python和JavaScript的主要区别在于类型系统和应用场景。1.Python使用动态类型,适合科学计算和数据分析。2.JavaScript采用弱类型,广泛用于前端和全栈开发。两者在异步编程和性能优化上各有优势,选择时应根据项目需求决定。

Python vs. JavaScript:选择合适的工具Python vs. JavaScript:选择合适的工具May 08, 2025 am 12:10 AM

选择Python还是JavaScript取决于项目类型:1)数据科学和自动化任务选择Python;2)前端和全栈开发选择JavaScript。Python因其在数据处理和自动化方面的强大库而备受青睐,而JavaScript则因其在网页交互和全栈开发中的优势而不可或缺。

Python和JavaScript:了解每个的优势Python和JavaScript:了解每个的优势May 06, 2025 am 12:15 AM

Python和JavaScript各有优势,选择取决于项目需求和个人偏好。1.Python易学,语法简洁,适用于数据科学和后端开发,但执行速度较慢。2.JavaScript在前端开发中无处不在,异步编程能力强,Node.js使其适用于全栈开发,但语法可能复杂且易出错。

JavaScript的核心:它是在C还是C上构建的?JavaScript的核心:它是在C还是C上构建的?May 05, 2025 am 12:07 AM

javascriptisnotbuiltoncorc; saninterpretedlanguagethatrunsonenginesoftenwritteninc.1)javascriptwasdesignedAsalightweight,解释edganguageforwebbrowsers.2)Enginesevolvedfromsimpleterterterpretpreterterterpretertestojitcompilerers,典型地提示。

JavaScript应用程序:从前端到后端JavaScript应用程序:从前端到后端May 04, 2025 am 12:12 AM

JavaScript可用于前端和后端开发。前端通过DOM操作增强用户体验,后端通过Node.js处理服务器任务。1.前端示例:改变网页文本内容。2.后端示例:创建Node.js服务器。

Python vs. JavaScript:您应该学到哪种语言?Python vs. JavaScript:您应该学到哪种语言?May 03, 2025 am 12:10 AM

选择Python还是JavaScript应基于职业发展、学习曲线和生态系统:1)职业发展:Python适合数据科学和后端开发,JavaScript适合前端和全栈开发。2)学习曲线:Python语法简洁,适合初学者;JavaScript语法灵活。3)生态系统:Python有丰富的科学计算库,JavaScript有强大的前端框架。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

VSCode Windows 64位 下载

VSCode Windows 64位 下载

微软推出的免费、功能强大的一款IDE编辑器

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )专业的PHP集成开发工具

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

将Eclipse与SAP NetWeaver应用服务器集成。

安全考试浏览器

安全考试浏览器

Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。