JavaScript 的深拷贝和浅拷贝

关于浅拷贝,按照常理理解,以及参考其他语言,我认为浅拷贝指的是和目标对象(数组)和原始对象(数组)指向同一块内存空间。赋值操作就是这种浅拷贝
var src = {x: 1, y: 2, z: 3};
var target = src;
target.y = 4;
console.log(src.y)           //4,相互影响

使用Object()创建对象的副本也是一样的

var person = {
    name: "Nicholas",
    friends: ["Shelby", "Court", "Van"]
};
var newPerson = Object(person);
newPerson.name = 'latency';
console.log(person.name);                //"latency"
console.log(person === newPerson)        //true

但是查了一下 jQuery 中 $.extend()方法的实现,发现如果不传入参数 true 指定深拷贝,那么$.extend()只复制了一层对象的属性。Object.assign()方法实现的也是这种浅拷贝。

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 = Array.isArray( copy ) ) ) ) {

					if ( copyIsArray ) {
						copyIsArray = false;
						clone = src && Array.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;
};
可以测试一下:
var src = {x: 1, y: {a: 2, b: 3}, z: [4, 5, 6]};
var target = $.extend({}, src);
target.x = 7;
target.y.a = 8;
target.z[1] = 9;
console.log(src);      //{x: 1, y: {a: 8, b: 3}, z: [4, 9, 6]};
可以看到,对于值为基本类型的x属性,src和target互相不影响,而它们的y和z属性,还是指向的同一块内存(实际上就是遍历了最外层的属性,把每个属性赋值给目标对象)

$.extend() 是基于对象的,如果是数组,slice(0)concat() 其实也只复制了一层(类似$.extend(false, {}, src)

var sarray = [1, [2, 3], {x: 4, y: 5}]
var tarray = sarray.slice(0);
tarray[0] = 6;
tarray[1][1] = 7;
tarray[2].y = 8;
console.log(sarray);        //[1, [2, 7], {x: 4, y: 8}]

var sarray = [1, [2, 3], {x: 4, y: 5}]
var tarray = sarray.concat();
tarray[0] = 6;
tarray[1][1] = 7;
tarray[2].y = 8;
console.log(sarray);        //[1, [2, 7], {x: 4, y: 8}]


深拷贝就没什么有争议的了,目标对象(数组)重新分配了一块内存区域,其每个属性(下标)的值和原始对象(数组)相同。

对于对象的深拷贝,可以使用$.extend()方法实现:

var src = {x: 1, y: {a: 2, b: 3}, z: [4, 5, 6]};
var target = $.extend(true, {}, src);
target.x = 7;
target.y.a = 8;
target.z[1] = 9;
console.log(src);        //{x: 1, y: {a: 2, b: 3}, z: [4, 5, 6]}
也可以先转化成 JSON对象,再转换为js对象。
var src = {x: 1, y: {a: 2, b: 3}, z: [4, 5, 6]};
var target = JSON.parse(JSON.stringify(src));
target.x = 7;
target.y.a = 8;
target.z[1] = 9;
console.log(src);        //{x: 1, y: {a: 2, b: 3}, z: [4, 5, 6]}

对于数组的深拷贝,最简单的方法还是使用JSON序列化

var sarray = [1, [2, 3], {x: 4, y: 5}]
var tarray = JSON.parse(JSON.stringify(sarray));
tarray[0] = 6;
tarray[1][1] = 7;
tarray[2].y = 8;
console.log(sarray);       [1, [2, 3], {x: 4, y: 5}] 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值