1、浅拷贝##
拷贝就是把父对象的属性,全部拷贝给子对象。
接下来,我们看一个拷贝的例子:
<pre>
function extendCopy(b) {
var a = {};
for (var i in b) {
a[i] = b[i];
}
return a;
}
</pre>
调用的时候,这样写:
<pre>
// 调用
var copyA = {
titleA: '标题A'
};
var copyB = extendCopy(copyA);
console.log(copyB.titleA); // 标题A
</pre>
但是,这样的拷贝有一个问题。那就是,如果父对象的属性等于数组或另一个对象,那么实际上,子对象获得的只是一个内存地址,而不是真正拷贝,因此存在父对象被篡改的可能。
接下来,我们看一个篡改的示例:
<pre>
function extendCopy(b) {
var a = {};
for (var i in b) {
a[i] = b[i];
}
return a;
}
// 调用
var copyA = {
arrayA: [1, 2, 3, 4]
};
var copyB = extendCopy(copyA);
copyB.arrayA.push(5);
console.log(copyA.arrayA); // [1, 2, 3, 4, 5]
</pre>
结果是增加了一个5。
所以,extendCopy() 只是拷贝了基本类型的数据,我们把这种拷贝叫做“浅拷贝”。
2、深拷贝##
因为浅深拷有如此弊端所以我们接下来看一下深拷贝
所谓”深拷贝”,就是能够实现真正意义上的数组和对象的拷贝。它的实现并不难,只要递归调用”浅拷贝”就行了。
<pre>
function deepCopy(p, c) {
var c = c || {};
for (var i in p) {
if (typeof p[i] === 'object') {
c[i] = (p[i].constructor === Array) ? [] : {};
deepCopy(p[i], c[i]);
} else {
c[i] = p[i];
}
}
return c;
}
// 调用
var copyA = {
arrayA: [1, 2, 3, 4]
};
var copyB = deepCopy(copyA);
copyB.arrayA.push(5);
console.log(copyA.arrayA); // [1, 2, 3, 4]
</pre>
这样就完成了拷贝;
拓展##
拓展一、数组的深浅拷贝
在使用JavaScript对数组进行操作的时候,我们经常需要将数组进行备份,事实证明如果只是简单的将它赋予其他变量,那么我们只要更改其中的任何一个,然后其他的也会跟着改变,这就导致了问题的发生。
<pre>
var arr = [1, 2, 3];
var copyarr = arr;
copyarr.push(4);
console.log(arr); // [1, 2, 3, 4]
console.log(copyarr); // [1, 2, 3, 4]
</pre>
像上面的这种直接赋值的方式就是浅拷贝,很多时候,这样并不是我们想要得到的结果,其实我们想要的是arr的值不变,不是吗?
方法一:js的slice函数
<pre>
对于array对象的slice函数,
返回一个数组的一段。(仍为数组)
arrayObj.slice(start, [end])
参数
arrayObj
必选项。一个 Array 对象。
start
必选项。arrayObj 中所指定的部分的开始元素是从零开始计算的下标。
end
可选项。arrayObj 中所指定的部分的结束元素是从零开始计算的下标。
说明
slice 方法返回一个 Array 对象,其中包含了 arrayObj 的指定部分。
slice 方法一直复制到 end 所指定的元素,但是不包括该元素。如果 start 为负,将它作为 length + start处理,此处 length 为数组的长度。如果 end 为负,就将它作为 length + end 处理,此处 length 为数组的长度。如果省略 end ,那么 slice 方法将一直复制到 arrayObj 的结尾。如果 end 出现在 start 之前,不复制任何元素到新数组中。
</pre>
方法二:js的concat方法
拓展二:$.extend()
用过jquery的朋友都知道jquery中有$.extend()。
$.extend( [deep ], target, object1 [, objectN ] )
** deep **类型: Boolean 如果是true,合并成为递归(又叫做深拷贝)。
** target 类型: Object 对象扩展。这将接收新的属性。 object1 **类型: Object 一个对象,它包含额外的属性合并到第一个参数.
** objectN **类型: Object 包含额外的属性合并到第一个参数
当我们提供两个或多个对象给$.extend(),对象的所有属性都添加到目标对象(target参数)。
如果只有一个参数提供给$.extend(),这意味着目标参数被省略。在这种情况下,jQuery对象本身被默认为目标对象。这样,我们可以在jQuery的命名空间下添加新的功能。这对于插件开发者希望向 jQuery 中添加新函数时是很有用的。
请记住,目标对象(第一个参数)将被修改,并且将通过$.extend()返回。然而,如果我们想保留原对象,我们可以通过传递一个空对象作为目标对象:
var object = $.extend({}, object1, object2);
在默认情况下,通过$.extend()合并操作不是递归的;如果第一个对象的属性本身是一个对象或数组,那么它将完全用第二个对象相同的key重写一个属性。这些值不会被合并。可以通过检查下面例子中 banana 的值,就可以了解这一点。然而,如果将 true 作为该函数的第一个参数,那么会在对象上进行递归的合并。
警告:不支持第一个参数传递 false 。
1、合并两个对象,并修改第一个对象。
<pre>
var obj1 = {
name: 'name1',
addr: {
p: '浙江',
c: '杭州'
},
age: 20
};
var obj2 = {
addr: {
d: '西湖'
},
sex: 1
};
$.extend(obj1, obj2);
console.log(JSON.stringify(obj1));
// {"name":"name1","addr":{"d":"西湖"},"age":20,"sex":1}
</pre>
2、采用递归方式合并两个对象,并修改第一个对象
<pre>
var obj1 = {
name: 'name1',
addr: {
p: '浙江',
c: '杭州'
},
age: 20
};
var obj2 = {
name: 'name2',
addr: {
d: '西湖'
},
sex: 1
};
$.extend(true, obj1, obj2);
console.log(JSON.stringify(obj1));
// {"name":"name2","addr":{"p":"浙江","c":"杭州","d":"西湖"},"age":20,"sex":1}
</pre>
3、合并 defaults 和 options 对象,并且不修改 defaults 对象。这也是常用的插件开发模式。
<pre>
var defaults = { isAuto: false, limit: 5, name: "foo" };
var options = { isAuto: true, name: "bar" };
var settings = $.extend( {}, defaults, options );
console.log(JSON.stringify( settings ));
//settings -- {"isAuto":true,"limit":5,"name":"bar"}
</pre>