JavaScript 深拷贝实现原理与代码解析

作者:闭包旅人

时间:2026-10-24

浏览:626

本文深入解析 JavaScript 中浅拷贝的缺陷,指出直接赋值会导致嵌套对象引用共享。通过演示 Prototype 库的 extend 函数,展示修改副本如何影响原对象。随后介绍利用 Object.prototype.toString 进行类型识别的方法,并给出一个递归实现的深拷贝函数,该函数能处理对象和数组的深层复制,同时避免循环引用导致的死循环。

文章目录

浅拷贝的缺陷分析

首先分析浅层复制存在的局限性,许多开发者可能都曾遇到过相关问题。


具体代码示例如下:

 
var oOriginal = {
memNum: 1, // number
memStr: "I am a string", // string
memObj: {
test1: "Old value" // we'll test
}, 
memArr: [ // array
"a string", // string member of array
{ // object member of array
test2: "Try changing me" // we'll test
} 
] 
}; 

这是一个结构较为复杂的对象,其中既包含嵌套的对象,也包含数组。我们可以借助 Prototype 库中著名的继承函数来简化操作。尽管该函数的本质是否属于真正的继承,以及 jQuery 的实现方式,此处暂不深究。


相关代码如下:

 
var extend = function(result, source) {
for (var key in source)
result[key] = source[key];
return result;
} 

测试程序如下:


具体代码:

 
var oCopy = extend({},oOriginal); // 浅拷贝
oCopy.memObj.test1 = "New value"; // 出现问题了,会反射到原对象上
alert(oOriginal.memObj.test1); // 结果副本与原本都一同被修改了
oCopy.memArr[1].test2 = "I am changed";
alert(oOriginal.memArr[1].test2); // 同样中招了

 <!doctype html> <html lang="en"> <head> <meta charset="utf-8" /> <meta content="IE=8" http-equiv="X-UA-Compatible"/> <title>javascript 深拷贝 by 司徒正美 </title>  </head> <body> <h1>javascript 深拷贝 by 司徒正美</h1> </body> </html> 

[Ctrl+A 全选 注:引入外部Js需再刷新一下页面才能执行]


类型识别与深拷贝实现

由此可见,若希望避免修改原始对象,必须对对象和数组进行特殊处理。为此,我们需要具备识别它们的能力。参考以下函数:


具体代码:

 
/** 
* type identification - By 司徒正美::cheng (MIT Licensed)
* http://www.cnblogs.com/rubylouvre/archive/2010/01/20/1652646.html
*/ 
var is = function (obj,type) {
var toString = Object.prototype.toString,undefined;
return (type === "Null" && obj === null) ||
(type === "Undefined" && obj === undefined ) ||
toString.call(obj).slice(8,-1) === type;
}; 

上述函数用于类型识别。typeof 关键字并不可靠,例如 Array 会返回 "object";instanceof 也不准确,它无法判定跨文档的 Array 实例。虽然利用 Object.prototype.toString.call 并非完美无缺,存在鸭子类型被仿造的可能,但相比以往的方法已可靠得多。


接下来正式介绍核心内容,我们的深拷贝函数会对值为对象或数组的键值对进行特殊处理。对于这类属性,程序会为目标对象创建新的对象或数组,然后逐层进行拷贝。值得注意的是,该实现并未使用 hasOwnProperty,这意味着原型链中可遍历的属性也会被一并复制。对于数组,由于 for...in 循环无法遍历数组索引元素,但能捕获附加在数组上的其他属性,因此这里仍使用 for...in 这种效率较低的方法。由于深拷贝需检测所有属性并可能创建新对象,这是一个重量级的方法,建议非必要不使用。


具体代码:

 
/** 
* type identification - By 司徒正美::cheng (MIT Licensed)
* http://www.cnblogs.com/rubylouvre/archive/2010/03/26/1696600.html
*/ 
dom.deepCopy = function(result, source){
for(var key in source) {
var copy = source[key];
if(result === copy) continue;//如window.window === window,会陷入死循环,需要处理一下
if(dom.is(copy,"Object")){
result[key] = arguments.callee(result[key] || {}, copy);
}else if(dom.is(copy,"Array")){
result[key] = arguments.callee(result[key] || [], copy);
}else{
result[key] = copy;
} 
} 
return result;
}; 

 <!doctype html> <html lang="en"> <head> <meta charset="utf-8" /> <meta content="IE=8" http-equiv="X-UA-Compatible"/> <title>javascript 深拷贝 by 司徒正美 </title>  </head> <body> <h1>javascript 深拷贝 by 司徒正美</h1> </body> </html> 

[Ctrl+A 全选 注:引入外部Js需再刷新一下页面才能执行]


作者:司徒正美

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章