浅拷贝
在进行拷贝对象的时候,如果属性是对象或者数组,则传递的是引用地址;若对这样的属性进行修改,那么父子对象之间就会发生关联。
1)‘=’赋值,将对象的引用赋值
var a={key1:"11111"}
function Copy(p){
var c ={};
for (var i in p){
c[i]=p[i]
}
return c;
}
a.key2 = ["小辉","小辉"]
var b = Copy(a);
b.key3 = "33333"
alert(b.key1)//11111
alert(b.key3)//33333
alert(a.key3);//undefined
b.key2.push("大辉")
alert(a.key2);//小辉,小辉,大辉
2)Object.assign(),是ES6的新函数
函数结构:Object.assign(target, ...sources)
参数:
target:目标对象
sources:任意多个源对象
返回值:返回目标对象
var obj = { a: {a: "hello", b: 21} };
var initalObj = Object.assign({}, obj);
initalObj.a.a = "changed";
console.log(obj.a.a); // changed
initalObj.b = 18;
console.log(obj.b); // undefined
深拷贝
实现复制的对象是另一个独立的对象,而不是原对象的引用。
1)直接复制,属性类型属于值类型
var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = { a: obj1.a, b: obj1.b, c: obj1.c };
obj2.b = 100;
console.log(obj1);// { a: 10, b: 20, c: 30 }
console.log(obj2);// { a: 10, b: 100, c: 30 }
2)JSON.stringify 把对象转成字符串,再 JSON.parse 把字符串转成新的对象
var obj1 = { body: { a: 10 } };
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.body.a = 20;
console.log(obj1);// { body: { a: 10 } }
console.log(obj2);// { body: { a: 20 } }
console.log(obj1 === obj2);// false
console.log(obj1.body === obj2.body);// false
只适用于可以转换成JSON格式的对象,像function、RegExp不可以使用此方法。
常用: Number,String,Boolean,Array,扁平对象
var obj1 = { fun: function(){ console.log(123) } };
var obj2 = JSON.parse(JSON.stringify(obj1));
console.log(typeof obj1.fun);// 'function'
console.log(typeof obj2.fun);// 'undefined' <-- 没复制
3)递归,把父对象中所有属于对象的属性类型都遍历赋给子对象
function deepClone(initalObj, finalObj) {
var obj = finalObj || {};
for (var i in initalObj) { // for in循环适用于对象、数组的遍历
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]); // 相当于 deepClone(prop,obj[i]); 降低耦合性
} else {
obj[i] = prop;
}
}
return obj;
}
var obj1 = {};
var obj2 = { a: {a: "hello", b: 21} };
deepClone(obj2, obj1);
console.log(obj1.a); // Object { a: "hello", b: 21 }
【补充1】
JS中的||
运算:
- 只要“||”前面为false,不管“||”后面是true还是false,都返回“||”后面的值。
- 只要“||”前面为true,不管“||”后面是true还是false,都返回“||”前面的值。
JS中的&&
运算:
- 只要“&&”前面是false,无论“&&”后面是true还是false,结果都将返“&&”前面的值;
- 只要“&&”前面是true,无论“&&”后面是true还是false,结果都将返“&&”后面的值;
【补充2】
在函数内部,有两个特殊的对象:arguments
和 this
。
其中, arguments 的主要用途是保存函数参数,有一个名叫 callee 的属性,该属性是一个指针,指向拥有这个 arguments 对象的函数。
arguments.callee()常用于递归,指向本函数。
4)Object.create()方法
function deepClone(initalObj, finalObj) {
var obj = finalObj || {};
for (var i in initalObj) { // for in循环适用于对象、数组的遍历
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;
}
var obj1 = {};
var obj2 = { a: {a: "hello", b: 21} };
deepClone(obj2, obj1);
console.log(obj1.a); // Object { a: "hello", b: 21 }
直接使用var newObj = Object.create(oldObj),可以达到深拷贝的效果。
5)jquery 提供的$.extend
var $ = require('jquery');
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = $.extend(true, {}, obj1);
console.log(obj1.b.f === obj2.b.f);// false
6)slice和concat对数组的深拷贝和浅拷贝
- 浅拷贝:
var arr1 = ["red","yellow","black"];
var arr2 = arr1;
arr2[1] = "green";
console.log("数组的原始值:" + arr1 ); // 数组的原始值:red,green,black
console.log("数组的新值:" + arr2); // 数组的新值:red,green,black
- 深拷贝:(仅适用于对不包含引用对象的一维数组的深拷贝)
// 方法一:slice(),数组截取,并返回一个新的数组
var arr1 = ["1","2","3"];
var arr2 = arr1.slice(0);
arr2[1] = "9";
console.log("数组的原始值:" + arr1 ); // 数组的原始值:1,2,3
console.log("数组的新值:" + arr2 ); // 数组的新值:1,9,3
// 方法二:concat(),合并数组,并返回一个新的数组
var arr1 = ["1","2","3"];
var arr2 = arr1.concat(); // concat()括号内,可以添加数组,或者数组元素作为参数
arr2[1] = "9";
console.log("数组的原始值:" + arr1 ); // 数组的原始值:1,2,3
console.log("数组的新值:" + arr2 ); // 数组的新值:1,9,3