关于浅拷贝,按照常理理解,以及参考其他语言,我认为浅拷贝指的是和目标对象(数组)和原始对象(数组)指向同一块内存空间。赋值操作就是这种浅拷贝。
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}]