JavaScript中的深拷贝与浅拷贝
总结
深拷贝和浅拷贝最根本的区别在于是否完整获取一个对象的复制实体,而不是引用。
实现浅拷贝的方法:
- Array.prototype.slice()
- Array.prototype.concat()
- Object.assign()
- 浅拷贝函数extendCopy()
实现深拷贝的方法:
- lodash的深拷贝_.cloneDeep(value)方法
- JSOM.parse()和JSON.stringify()方法
- 深拷贝递归函数实现深拷贝
- JQuery中的extend方法
浅拷贝
在定义一个对象或数组时,变量存放的往往只是一个地址。使用对象拷贝时,如果属性是数组或对象,其实质为传递引用地址。因此子对象在访问该属性时,会根据地址回溯到父对象指向的堆内存中,即父子对象发生了关联,两者的属性值会指向同一内存空间。
方法一:Array.prototype.slice()
/* 方法一:Array.prototype.slice()方法返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变。*/
/* 当属性中无引用数据类型 */
let a = [1,2,3,4]
let b = a.slice();
a[0] = 5;
console.log(a); // => [5,2,3,4]
console.log(b); // => [1,2,3,4]
/* 当属性中含有引用数据类型 */
let a = [0,1,[2,3],4]
let b = a.slice();
a[0] = 5;
a[2][0] = 6;
console.log(a); // => [5,1,[6,3],4]
console.log(b); // => [0,1,[6,3],4]
方法二:Array.prototype.concat()
/* 方法二:Array.prototype.concat()方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。*/
/* 当属性中无引用数据类型 */
let a = [1,2,3,4]
let b = []
b = b.concat(a);
a[0] = 5;
console.log(a); // => [5,2,3,4]
console.log(b); // => [1,2,3,4]
/* 当属性中含有引用数据类型 */
let a = [0,1,{name:"koco"},4]
let b = a.slice();
a[0] = 5;
a[2].name = "soco";
console.log(a); // => [5,1,{name:"soco"},4]
console.log(b); // => [0,1,{name:"soco"},4]
方法三:Object.assign()
/*方法三:Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。*/
let a = {
name: "Koco",
interest: ["羽毛球", "台球"]
}
let b = Object.assign({},a);
b.interest.push("喝茶");
b.name = "Soco";
console.log(a); // => {name: "Koco",interest:["羽毛球", "台球", "喝茶"]}
console.log(b); // => {name: "Soco",interest:["羽毛球", "台球", "喝茶"]}
方法四:简单的拷贝函数
/*方法四:简单的拷贝函数 */
function extendCopy(p){
var c = {};
for (var i in p) {
c[i] = p[i];
}
return c;
}
/* 定义变量 */
let a = {
name:"Koco",
interest:["羽毛球","台球"]
}
let b = extendCopy(a)
/* 增加新属性 */
b.age = 12;
console.log("a",a); // => {interest:["羽毛球","台球"] name: "Koco"}
console.log("b",b); // => {age: 12,interest:["羽毛球","台球"],name: "Koco"}
/* 修改基本类型属性 */
b.name = "Scooby"
console.log("a",a); // => {interest:["羽毛球", "台球"],name: "Koco"}
console.log("b",b); // => {interest:["羽毛球", "台球"],name: "Scooby"}
/* 修改引用类型属性 */
b.interest.push("喝茶");
console.log("a",a); // => {interest:["羽毛球", "台球", "喝茶"],name: "koco"}
console.log("b",b); // => {interest:["羽毛球", "台球", "喝茶"],name: "koco"}
深拷贝
深拷贝时父子对象没有任何关联,在栈和堆中重新创建一个全新的内存用于存放复制的对象。
方法一:利用lodash的深拷贝_.cloneDeep(value)实现深拷贝
/* 方法一:利用lodash的深拷贝_.cloneDeep(value)方法实现深拷贝 */
var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);// => false
方法二:利用JSOM.parse和JSON.stringify实现深拷贝
/* 方法二:利用JSOM.parse和JSON.stringify实现深拷贝 */
function deepCopy(tempObj){
let tempClone = JSON.stringify(tempObj)
return JSON.parse(tempClone);
}
let a = {
name: "Koco",
interest: ["羽毛球", "台球"]
}
let b = deepCopy(a);
/* 修改引用类型属性 */
b.interest.push("喝茶");
console.log("a",a.interest); // => ["羽毛球", "台球"]
console.log("b",b.interest); // => ["羽毛球", "台球", "喝茶"]
方法三:利用深拷贝递归函数实现深拷贝
/* 方法三:利用深拷贝递归函数实现深拷贝 */
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;
}
let a = {
name: "Koco",
interest: ["羽毛球", "台球"]
}
let b = {};
b = deepCopy(a, b);
/* 修改引用类型属性 */
b.interest.push("喝茶");
console.log("a",a.interest); // => ["羽毛球", "台球"]
console.log("b",b.interest); // => ["羽毛球", "台球", "喝茶"]
方法四:利用JQuery中的extend实现深拷贝
/* 方法四:利用JQuery中的extend方法实现深拷贝 */
let a = {
name: "Koco",
interest: ["羽毛球", "台球"]
}
let b=$.extend(true,[],a);
/* 修改引用类型属性 */
b.interest.push("喝茶");
console.log("a",a.interest); // => ["羽毛球", "台球"]
console.log("b",b.interest); // => ["羽毛球", "台球","喝茶"]