目录
一、概念
1、值传递和引用传递
值传递的是基本数据类型(Number,String,Boolean,Null,Undefined),直接用 = 传递,一般存放于内存中的栈区,存取速度快,存放量小;数据大小确定。
引用传递的是引用类型(Object,Array,Function,Symbol),一般存放与内存中的堆区,存取速度慢,存放量大,其引用指针存于栈区,并指向引用本身。
2、深拷贝和浅拷贝
深拷贝和浅拷贝是相对于引用类型而言的,因为基本类型赋值都是深拷贝。
浅拷贝: 对于对象来说,浅拷贝是对对象地址的复制, 也就是拷贝的结果是两个对象指向同一个地址指两指两个js 对象指向同一个内存地址,其中一个改变会影响另一个.
深拷贝: 深拷贝开辟一个新的栈,两个对象对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性。指复制后的新对象重新指向一个新的内存地址,两个对象改变互不影响。
二、浅拷贝
浅拷贝是对对象地址的复制, 也就是拷贝的结果是两个对象指向同一个内存地址。
1、‘=’ 赋值
var a = {
name: "a",
book: [1, 2, 3],
};
var b = a; // 指向同一个地址
b.name = "b";
b.book[0] = 100;
console.log(a); // { name: 'b', book: [ 100, 2, 3 ] }
2、Object.assign()
Object.assign()
方法将所有可枚举(Object.propertyIsEnumerable()
返回 true)和自有(Object.hasOwnProperty()
返回 true)属性从一个或多个源对象复制到目标对象,返回修改后的对象。
Object.assign() 进行的是浅拷贝,但是第一层处理的是深拷贝。
var target = { a: { a: "aaaa", b: 21 }, name: "a" };
var source = { a: { a: "bbbb", b: 21 }, name: "b" };
var initalObj = Object.assign(target, source);
console.log(initalObj); // { a: { a: 'bbbb', b: 21 }, name: 'b' }
source.a.a = "changed";
source.name = "c";
console.log(initalObj.a === source.a); // true
console.log(source); // { a: { a: 'changed', b: 21 }, name: 'c' }
console.log(initalObj); // { a: { a: 'changed', b: 21 }, name: 'b' } name没被修改
3、解构赋值
对象的解构赋值用于从一个对象取值,相当于将目标对象自身的所有可遍历的(enumerable)、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面。
解构赋值的拷贝是浅拷贝
var source = { x: 1, y: 2, a: 3, b: 4 };
var { x, y, ...z } = source;
console.log(z); // { a: 3, b: 4 }
var obj = { a: { b: 1 } };
var { ...o } = obj;
obj.a.b = 2;
console.log(o); // { a: { b: 2 } }
4、concat()
不修改原数组,拷贝原数组生成一个新数组(堆里面内存开辟一个新空间),数组内部浅拷贝。
var arr1 = [10, 20, { a: 1 }];
var arr2 = [30, 40, { b: 2 }];
var newArr = arr1.concat(arr2);
console.log(newArr); // [ 10, 20, { a: 1 }, 30, 40, { b: 2 } ]
console.log(arr1); // [ 10, 20, { a: 1 } ] 不影响原来的数组
arr1[2].a = 3;
arr2[2].b = 4;
console.log(newArr); // [ 10, 20, { a: 3 }, 30, 40, { b: 4 } ]
5、slice()
不修改原数组,返回新数组对象,由 begin 和 end 决定的原数组的浅拷贝。
var arr1 = [10, 20, { a: 1 }, 30, 40, 50, 60];
var newArr = arr1.slice(1, 4);
console.log(newArr); // [ 20, { a: 1 }, 30 ]
console.log(arr1); // [ 10, 20, { a: 1 }, 30, 40, 50, 60 ]
arr1[2].a = 3;
console.log(newArr); // [ 20, { a: 3 }, 30 ]
三、深拷贝
1、JSON.stringify 和 JSON.parse
用 JSON.stringify 把对象转换成字符串,再用 JSON.parse 把字符串转换成新的对象。
var obj = {
name: "aaa",
value: "JS",
};
var newObj = JSON.parse(JSON.stringify(obj));
console.log(newObj === obj); // false
newObj.name = "bbb";
console.log(newObj); // { name: 'bbb', value: 'JS' } 深拷贝了一份obj
console.log(obj); // { name: 'aaa', value: 'JS' }
缺点:
- 如果obj里面有时间对象Date,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式,而不是时间对象。
- 如果obj里有function,Symbol类型,undefined,则序列化的结果会把函数、Symbol类型、undefined丢失。
- 如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null。
- 如果obj里有RegExp、Error对象,则序列化的结果将只得到空对象;
var oldObj = {
a: "1",
b: undefined,
c: null,
d: {
child: "child",
},
e: Symbol(),
f: NaN,
g: Infinity,
h: -Infinity,
i: /runoob/i,
date: new Date(),
fn: function () {
console.log("ok");
},
};
const newObj = JSON.parse(JSON.stringify(oldObj));
console.log(oldObj);
console.log(JSON.stringify(oldObj));
console.log(newObj);
// 输出:
{
a: '1',
b: undefined,
c: null,
d: { child: 'child' },
e: Symbol(),
f: NaN,
g: Infinity,
h: -Infinity,
i: /runoob/i,
date: 2022-07-21T03:17:09.820Z,
fn: [Function: fn]
}
{"a":"1","c":null,"d":{"child":"child"},"f":null,"g":null,"h":null,"i":{},
"date":"2022-07-21T03:17:09.820Z"}
{
a: '1',
c: null,
d: { child: 'child' },
f: null,
g: null,
h: null,
i: {},
date: '2022-07-21T03:17:09.820Z'
}
四、手写深拷贝-深度遍历
function deepClone(obj) {
if (obj && typeof obj === "object") {
let result = obj instanceof Array ? [] : {};
for (var i in obj) {
if (obj.hasOwnProperty(i)) {
if (obj[i] instanceof Date) {
// Date循环没有内容,走deepClone(obj[i])值会变为{}
result[i] = obj[i];
} else if (obj[i] instanceof RegExp) {
result[i] = new RegExp(obj[i]);
} else {
result[i] = obj[i] instanceof Object ? deepClone(obj[i]) : obj[i];
}
}
}
return result;
} else {
return obj;
}
}
var oldObj = {
a: "1",
b: undefined,
c: null,
d: {
child: "child",
},
e: Symbol(),
f: NaN,
g: Infinity,
h: -Infinity,
i: /runoob/i,
date: new Date(),
fn: function () {
console.log("ok");
},
};
console.log(oldObj);
console.log(deepClone(oldObj));
{
a: '1',
b: undefined,
c: null,
d: { child: 'child' },
e: Symbol(),
f: NaN,
g: Infinity,
h: -Infinity,
i: /runoob/i,
date: 2022-07-21T03:24:33.689Z,
fn: [Function: fn]
}
{
a: '1',
b: undefined,
c: null,
d: { child: 'child' },
e: Symbol(),
f: NaN,
g: Infinity,
h: -Infinity,
i: /runoob/i,
date: 2022-07-21T04:00:52.884Z,
fn: [Function: fn]
}