在JavaScript中,关于Object
和Array
这类援用范例值,当从一个变量向另一个变量复制援用范例值时,这个值的副本实际上是一个指针,两个变量指向同一个堆对象,转变个中一个变量,另一个也会受到影响。
这类拷贝分为两种状况:拷贝援用和拷贝实例,也就是我们说的浅拷贝和深拷贝
浅拷贝(shallow copy)
拷贝原对象的援用,这是最简朴的浅拷贝。
// 对象
var o1 = {a: 1};
var o2 = o1;
console.log(o1 === o2); // =>true
o2.a = 2;
console.log(o1.a); // => 2
// 数组
var o1 = [1,2,3];
var o2 = o1;
console.log(o1 === o2); // => true
o2.push(4);
console.log(o1); // => [1,2,3,4]
拷贝原对象的实例,然则对其内部的援用范例值,拷贝的是其援用,经常使用的就是如jquey中的$.extend({}, obj);
Array.prototype.slice()
和Array.prototype.concat()
都邑返回一个数组或许对象的浅拷贝,举个例子:
var o1 = ['darko', {age: 22}];
var o2 = o1.slice(); // 依据Array.prototype.slice()的特征,这里会返回一个o1的浅拷贝对象
console.log(o1 === o2); // => false,申明o2拷贝的是o1的一个实例
o2[0] = 'lee';
console.log(o1[0]); // => "darko" o1和o2内部包括的基础范例值,复制的是其实例,不会相互影响
o2[1].age = 23;
console.log(o1[1].age); // =>23 o1和o2内部包括的援用范例值,复制的是其援用,会相互影响
可以经由过程Array.prototype.slice()
或jQuery
中的$.extend({}, obj)
完成对一个数组或许对象的浅拷贝,我们也可以本身写一个简朴浅拷贝函数来加深对浅拷贝的明白、
// 浅拷贝完成,仅供参考
function shallowClone(source) {
if (!source || typeof source !== 'object') {
throw new Error('error arguments');
}
var targetObj = source.constructor === Array ? [] : {};
for (var keys in source) {
if (source.hasOwnProperty(keys)) {
targetObj[keys] = source[keys];
}
}
return targetObj;
}
深拷贝(deep copy)
深拷贝也就是拷贝出一个新的实例,新的实例和之前的实例互不影响,深拷贝的完成有几种要领,起首我们可以借助jQuery,lodash等第三方库完成一个深拷贝实例。在jQuery中可以经由过程增加一个参数来完成递归extend,挪用$.extend(true, {}, ...)
就可以完成一个深拷贝。
我们也可以本身完成一个深拷贝的函数,平常有两种体式格局,一种就是用递归
的体式格局来做,另有一种是运用JSON.stringify
和JSON.parse
来做,这两种体式格局各有好坏,先来看看递归的要领怎么做。
jQuery中的extend要领基础的就是根据这个思绪完成的,然则没有办法处置惩罚源对象内部轮回援用
的题目,同时对Date,Funcion等范例值也没有完成真正的深度复制,然则这些范例的值在从新定义的时刻平常都是直接掩盖,所以也不会对源对象产生影响,从肯定水平上来讲也算是完成了一个深拷贝。
// 递归完成一个深拷贝
function deepClone(source){
if(!source || typeof source !== 'object'){
throw new Error('error arguments', 'shallowClone');
}
var targetObj = source.constructor === Array ? [] : {};
for(var keys in source){
if(source.hasOwnProperty(keys)){
if(source[keys] && typeof source[keys] === 'object'){
targetObj[keys] = source[keys].constructor === Array ? [] : {};
targetObj[keys] = deepClone(source[keys]);
}else{
targetObj[keys] = source[keys];
}
}
}
return targetObj;
}
// test example
var o1 = {
arr: [1, 2, 3],
obj: {
key: 'value'
},
func: function(){
return 1;
}
};
var o3 = deepClone(o1);
console.log(o3 === o1); // => false
console.log(o3.obj === o1.obj); // => false
console.log(o2.func === o1.func); // => true
另有一种完成深拷贝的体式格局是运用JSON对象
中的parse
和stringify
,JOSN对象中的stringify可以把一个js对象序列化为一个JSON字符串,parse可以把JSON字符串反序列化为一个js对象,经由过程这两个要领,也可以完成对象的深复制。
我们从下面的例子就可以看到,源对象的要领在拷贝的过程当中丧失了,这是由于在序列化JavaScript对象时,一切函数和原型成员会被故意疏忽
,这个完成可以满足一些比较简朴的状况,可以处置惩罚JSON花样所能示意的一切数据范例,同时如果在对象中存在轮回运用的状况也没法正确处置惩罚。
// 运用JSON序列化完成一个深拷贝
function deepClone(source){
return JSON.parse(JSON.stringify(source));
}
var o1 = {
arr: [1, 2, 3],
obj: {
key: 'value'
},
func: function(){
return 1;
}
};
var o2 = deepClone(o1);
console.log(o2); // => {arr: [1,2,3], obj: {key: 'value'}}