视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
JavaScript中对象的深拷贝
2020-11-27 20:27:18 责编:小采
文档

对象的深拷贝与浅拷贝的区别如下:

浅拷贝:仅仅复制对象的引用,而不是对象本身;
深拷贝:把复制的对象所引用的全部对象都复制一遍。

一. 浅拷贝的实现

浅拷贝的实现方法比较简单,只要使用是简单的复制语句即可。

1.1 方法一:简单的复制语句

/* ================ 浅拷贝 ================ */
function simpleClone(initalObj) {
 var obj = {};
 for ( var i in initalObj) {
 obj[i] = initalObj[i];
 }
 return obj;
}

客户端调用

/* ================ 客户端调用 ================ */
var obj = {
 a: "hello",
 b: {
 a: "world",
 b: 21
 },
 c: ["Bob", "Tom", "Jenny"],
 d: function() {
 alert("hello world");
 }
}
var cloneObj = simpleClone(obj); // 对象拷贝
 
console.log(cloneObj.b); // {a: "world", b: 21}
console.log(cloneObj.c); // ["Bob", "Tom", "Jenny"]
console.log(cloneObj.d); // function() { alert("hello world"); }
 
// 修改拷贝后的对象
cloneObj.b.a = "changed";
cloneObj.c = [1, 2, 3];
cloneObj.d = function() { alert("changed"); };
 
console.log(obj.b); // {a: "changed", b: 21} // // 原对象所引用的对象被修改了
 
console.log(obj.c); // ["Bob", "Tom", "Jenny"] // 原对象所引用的对象未被修改
console.log(obj.d); // function() { alert("hello world"); } // 原对象所引用的函数未被修改

1.2 方法二:Object.assign()

Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。

var obj = { a: {a: "hello", b: 21} }; 
var initalObj = Object.assign({}, obj); 
initalObj.a.a = "changed"; 
console.log(obj.a.a); // "changed"

二. 深拷贝的实现

要实现深拷贝有很多办法,有最简单的 JSON.parse() 方法,也有常用的递归拷贝方法,和ES5中的 Object.create() 方法。

2.1 方法一:使用 JSON.parse() 方法

要实现深拷贝有很多办法,比如最简单的办法是使用 JSON.parse():

/* ================ 深拷贝 ================ */
function deepClone(initalObj) {
 var obj = {};
 try {
 obj = JSON.parse(JSON.stringify(initalObj));
 }
 return obj;
}
/* ================ 客户端调用 ================ */
var obj = {
 a: {
 a: "world",
 b: 21
 }
}
var cloneObj = deepClone(obj);
cloneObj.a.a = "changed";
 
console.log(obj.a.a); // "world"

这种方法简单易用。

但是这种方法也有不少坏处,譬如它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。

这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,即那些能够被 json 直接表示的数据结构。RegExp对象是无法通过这种方式深拷贝。

2.2 方法二:递归拷贝

代码如下:

/* ================ 深拷贝 ================ */
function deepClone(initalObj, finalObj) {
 var obj = finalObj || {};
 for (var i in initalObj) {
 if (typeof initalObj[i] === 'object') {
 obj[i] = (initalObj[i].constructor === Array) ? [] : {};
 arguments.callee(initalObj[i], obj[i]);
 } else {
 obj[i] = initalObj[i];
 }
 }
 return obj;
}

上述代码确实可以实现深拷贝。但是当遇到两个互相引用的对象,会出现死循环的情况。

为了避免相互引用的对象导致死循环的情况,则应该在遍历的时候判断是否相互引用对象,如果是则退出循环。

改进版代码如下:

/* ================ 深拷贝 ================ */
function deepClone(initalObj, finalObj) {
 var obj = finalObj || {};
 for (var i in initalObj) {
 var prop = initalObj[i];
 
 // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
 if(prop === obj) {
 continue;
 }
 
 if (typeof prop === 'object') {
 obj[i] = (prop.constructor === Array) ? [] : {};
 arguments.callee(prop, obj[i]);
 } else {
 obj[i] = prop;
 }
 }
 return obj;
}

2.3 方法三:使用Object.create()方法

直接使用var newObj = Object.create(oldObj),可以达到深拷贝的效果。

/* ================ 深拷贝 ================ */
function deepClone(initalObj, finalObj) {
 var obj = finalObj || {};
 for (var i in initalObj) {
 var prop = initalObj[i];
 
 // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
 if(prop === obj) {
 continue;
 }
 
 if (typeof prop === 'object') {
 obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
 } else {
 obj[i] = prop;
 }
 }
 return obj;
}

三. 参考:jQuery.extend()方法的实现

jQuery.js的jQuery.extend()也实现了对象的深拷贝。下面将官方代码贴出来,以供参考。

官方链接地址:https://github.com/jquery/jquery/blob/master/src/core.js。

jQuery.extend = jQuery.fn.extend = function() {
 var options, name, src, copy, copyIsArray, clone,
 target = arguments[ 0 ] || {},
 i = 1,
 length = arguments.length,
 deep = false;
 
 // Handle a deep copy situation
 if ( typeof target === "boolean" ) {
 deep = target;
 
 // Skip the boolean and the target
 target = arguments[ i ] || {};
 i++;
 }
 
 // Handle case when target is a string or something (possible in deep copy)
 if ( typeof target !== "object" && !jQuery.isFunction( target ) ) {
 target = {};
 }
 
 // Extend jQuery itself if only one argument is passed
 if ( i === length ) {
 target = this;
 i--;
 }
 
 for ( ; i < length; i++ ) {
 
 // Only deal with non-null/undefined values
 if ( ( options = arguments[ i ] ) != null ) {
 
 // Extend the base object
 for ( name in options ) {
 src = target[ name ];
 copy = options[ name ];
 
 // Prevent never-ending loop
 if ( target === copy ) {
 continue;
 }
 
 // Recurse if we're merging plain objects or arrays
 if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
 ( copyIsArray = jQuery.isArray( copy ) ) ) ) {
 
 if ( copyIsArray ) {
 copyIsArray = false;
 clone = src && jQuery.isArray( src ) ? src : [];
 
 } else {
 clone = src && jQuery.isPlainObject( src ) ? src : {};
 }
 
 // Never move original objects, clone them
 target[ name ] = jQuery.extend( deep, clone, copy );
 
 // Don't bring in undefined values
 } else if ( copy !== undefined ) {
 target[ name ] = copy;
 }
 }
 }
 }
 
 // Return the modified object
 return target;
};

这篇文章主要是介绍js关于深拷贝的内容,其它的内容可以查看PHP中文网以前发表的文章

更多JavaScript 中对象的深拷贝相关文章请关注PHP中文网!

下载本文
显示全文
专题