对象引用和复制-Object基础
把对象赋值给变量时,并不是在变量里存储了对象,而是在变量里存储了对象在内存中的地址。
所以对象的存储和引用的存储不是绑定的。
let user = { name: 'John' };
let admin = user;
admin.name = 'Pete'; // 通过 "admin" 引用来修改
alert(user.name); // 'Pete',修改能通过 "user" 引用看到
user变量和admin变量就像带有两把钥匙的柜子,使用其中一把钥匙打开柜子做了变动,另一把钥匙后面打开柜子是,会看到之前的变动。
字面量对象的相等性
只有当两个变量指向同一个对象时,才是相等的。
克隆与合并
最简单的方法:
let a = {
age: 99,
likes: ["game", "book"],
action: function () {
console.log(this.likes);
},
}
let b = {
age: 9999,
link2: a,
}
a["link1"] = b;
a.action();
let copy = {};
for (let k in a) {
copy[k] = a[k];
}
for (let k in b) {
copy[k] = b[k];
}
console.log(copy); //循环引用可以正常复制, 不会报错
copy.action();
console.log(copy.action === a.action); // true
console.log(copy.likes === a.likes); // true
缺点是引用类型(包括函数)都是直接复制的引用。
Object.assign
与前面的简单方法一样。缺点也一样。
let copy = Object.assign({}, a, b);
深层克隆
如果某个属性的值也是一个对象,那么也要复制它的结构。这就叫“深拷贝“。
let a = [1,2,3];
let f = function(){
}
let d = new Date();
typeof a //'object'
typeof f //'function'
typeof d //'object'
由于typeof无法判断object和array。可以使用object.prototype.toString
方法,但是直接使用实例对象.toString()
调用时,数组还是返回的"[object Object]"
,时间Date的实例对象返回的是日期字符串。
所以,深复制的关键是递归调用和类型判断
let a = [1,2,3];
let f = function(){
}
let d = new Date();
ff.toString(); // "function ff() {}"
d.toString(); // "Mon Aug 02 2021 17:25:28 GMT+0800 (中国标准时间)"
a.toString(); // "1,2,3"
原因是因为这些对象的的原型上已经覆盖了toString
方法。
类型判断的一个解决方法是,使用Object.prototype.toString
方法时绑定当前实例对象Object的toString方法上。
function typeValue(value) {
let typeMap = {
'[object Array]': 'array',
'[object Function]': 'function',
'[object Object]': 'object',
}
return typeMap[Object.prototype.toString.call(obj)];
}
let a = [1,2,3];
let f = function(){
}
let d = new Date();
typeValue(f); // 'function'
typeValue(a); // 'array'
Object.prototype.toString.call(d); // '[object Date]'
在进行值的复制时,就可以判断这个值是数组还是其他类型了。
function deepClone(value) {
let copy; // 最终返回的复制后的值
function clone(value) {
if (typeValue(value) === 'array') {
let temp = [];
for (let e of value) {
// 属性值递归clone
temp.push(clone(e));
}
return temp;
}
if (typeValue(value) === 'object') {
let temp = {};
// 加上这一句是因为assign方法可以看到symbol属性,但是for...in看不到
Object.assign(temp, value);
for (let key in value) {
// 数组元素还需要再进行clone
temp[key] = clone(value[key]);
}
return temp;
}
// 既不是数组,也不是对象Object
return value;
}
copy = clone(value);
return copy;
}
此方法可以深复制一个嵌套了多个深度的对象。
由于Symbol
属性的隐藏性,对for...in不可见,导致不能复制Symbol类型的属性。
而Object.assign
属性却可以复制Symbol属性