对象相互赋值的一些关系,分别包括:
-
引用赋值:指向同一个对象,相互之间会影响;
-
对象的浅拷贝:只是浅层的拷贝,内部引入对象时,依然会相互影响;
-
对象的深拷贝:两个对象不再有任何关系,不会相互影响;
1 引用赋值
const info = {
name: "why",
age: 18,
friend: {
name: "kobe",
},
};
//1.操作1:引用赋值
// 本质就是内存里面将对象的地址给他,就是地址赋值
const obj1 = obj
2. 浅拷贝
两种方法:
const info = {
name: "why",
age: 18,
friend: {
name: "kobe",
},
};
//2.操作二:浅拷贝
//1.通过展开运算符进行浅拷贝
const obj2 = { ...info };
obj2.name = "james";
console.log(info.name); //why 没改变元对象
obj2.friend.name = "james";
console.log(info.friend.name); //james 改变了元对象
//2.通过Object.assign(target,source)将source对象拷贝到target对象
const obj3 = Object.assign({}, info);
obj3.name = "curry";
console.log(info.name); //why
obj2.friend.name = "james";
console.log(info.friend.name); //james
3. 深拷贝
const info = {
name: "why",
age: 18,
friend: {
name: "kobe",
},
foo: function () {
console.log("foo");
},
[Symbol()]: "aaa",
};
//1.操作三:深拷贝 完全不同的对象,影响内存
//所以写函数的时候,不要随便改变传进来的参数。
//1.JSON方法
const infoJson = JSON.stringify(info);
console.log(infoJson);
//{"name":"why","age":18,"friend":{"name":"kobe"}} 没有function和symbol
const obj4 = JSON.parse(infoJson);
console.log(obj4);
obj4.friend.name = "curry";
console.log(info.friend.name); //kobe
/*
json方法的缺点就是如果对象里面有function和symbole的时候他不知道怎么解析,strigify的时候就自动忽略
*/
//2.自己编写深拷贝函数
前面我们已经可以通过一种方法来实现深拷贝了:JSON.parse
-
这种深拷贝的方式其实对于函数、Symbol等是无法处理的;
-
并且如果存在对象的循环引用,也会报错的;
const info = {
name: "why",
age: 18,
friend: {
name: "kobe",
},
foo: function () {
console.log("foo");
},
[Symbol()]: "aaa",
// obj:info
//1.这种写法在开发中不被允许,浏览器直接报错,info还没有初始化完成就使用info
};
info.obj = info;
console.log(info);
//2.这样写的化就没问题
/*
window是一个对象,里面有一个属性,叫做window,这个window属性就是指向window对象。
*/
console.log(window.window == window); //true
//于是可以无限调用
console.log(window.window.window.window.window);
//但是这种引用是不能倍JSON处理的。
JSON没办法处理对象属性的循环引用
const info = {
name: "why",
age: 18,
friend: {
name: "kobe",
},
foo: function () {
console.log("foo");
},
[Symbol()]: "aaa",
// obj:info
//1.这种写法在开发中不被允许,浏览器直接报错,info还没有初始化完成就使用info
};
info.obj = info;
const objJson = JSON.stringify(info);
//Uncaught TypeError: Converting circular structure to JSON
//直接报错。
所以使用JSON实现深拷贝问题比较大。我们需要自己实现深拷贝。
23.3.1. 手写深拷贝(基本使用)
如果想实现浅拷贝和深拷贝
function myCopy(obj, isDeep = true) {
if (isDeep) {
/* ... */
} else {
return { ...obj };
}
}
先简单封装一个函数
/*
js有8种数据类型
1.Number
2.String
3.Boolean
4.Undefined
5.Null
6.Object
7.BigInt
8.Symbol
*/
/* 需求:判断一个标识符是不是对象类型 */
function isObject(value) {
//null object function array
//null-->我们希望你返回false
//函数-->我们希望你返回true
//对与typeof null返回的是object,function返回的是function,object和array返回的是object
const valueType = typeof value;
return (
value !== null && (valueType === "object" || valueType == "function")
);
}
const name = "why";
const age = 18;
const foo = {};
const bar = function () {};
const arr = [];
console.log(isObject(null)); // false
console.log(isObject(bar)); // true
console.log(isObject(name)); // false
console.log(isObject(foo)); // true
console.log(isObject(arr)); // true
function isObject(value) {
const valueType = typeof value;
return (
value !== null && (valueType === "object" || valueType == "function")
);
}
const info = {
name: "why",
age: 18,
friend: {
name: "kobe",
address: {
name: "洛杉矶",
detail: "斯坦普斯中心",
},
},
};
function deepCopy(originValue) {
//1.如果是原始类型,直接返回
if (!isObject(originValue)) {
return originValue;
}
//2.如果是对象类型,才需要创建一个新对象
const newObj = {};
for (const key in originValue) {
newObj[key] = deepCopy(originValue[key]);
}
return newObj;
}
/*const newObj = deepCopy("abc");
console.log(newObj); //abc */
const newObj = deepCopy(info);
console.log(newObj);
info.friend.address.name = "new address name";
console.log(newObj.friend.address.name); //洛杉矶
3.2. 手写深拷贝(数组拷贝)
只是多写一行判断
const newObj = Array.isArray(originValue) ? [] : {};
function isObject(value) {
const valueType = typeof value;
return (
value !== null && (valueType === "object" || valueType == "function")
);
}
const books = [
{
name: "黄金时代",
price: 28,
desc: { intro: "这本书不错", info: "这本书讲了一个很有意思的故事" },
},
{ name: "你不知道JavaScript", price: 99 },
];
function deepCopy(originValue) {
//1.如果是原始类型,直接返回
if (!isObject(originValue)) {
return originValue;
}
//2.如果是对象类型,才需要创建一个新对象
const newObj = Array.isArray(originValue) ? [] : {};
for (const key in originValue) {
newObj[key] = deepCopy(originValue[key]);
}
return newObj;
}
const newBooks = [...books];
newBooks[0].price = 88;
console.log(books[0].price); //88 浅拷贝
const newObj = deepCopy(books);
console.log(newObj);
3.3. 手写深拷贝(其他类型)
1. 值是set数据结构的时候
set存在的问题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
function isObject(value) {
const valueType = typeof value;
return (
value !== null && (valueType == "object" || valueType == "function")
);
}
const set = new Set(["abc", "cba", "nba"]);
const info = {
name: "why",
age: 18,
friend: {
name: "kobe",
address: {
name: "洛杉矶",
detail: "斯坦普斯中心",
},
},
set: set,
};
function deepCopy(originValue) {
if (!isObject(originValue)) {
return originValue;
}
const newObj = Array.isArray(originValue) ? [] : {};
//确定value是一个对象了以后:进行遍历
for (const key in originValue) {
newObj[key] = deepCopy(originValue[key]);
}
return newObj;
}
console.log(deepCopy(info));
/*
如果有对象里面有set属性的化,就是属性的value是set数据类型的化,深拷贝的结果是
set : {} 拷贝的set是空数组
*/
//console.log(JSON.stringify(info));
//{"name":"why","age":18,"friend":{"name":"kobe","address":{"name":"洛杉矶","detail":"斯坦普斯中心"}},"set":{}}
//console.log(JSON.parse(JSON.stringify(info))); //set: {} 使用JSON方法深拷贝的也是个空对象
/*
原因是set用isArray判断,不是一个对象,所以创建一个数组放它。
然后用for in遍历他
*/
for (const item in set) {
console.log(item);
}
/* 没有任何打印,所以对于set这个属性的值来说,就是一个空数组 */
</script>
</body>
</html>
解决方法
//判断是不是set对象
console.log(Object.prototype.toString.call(set)); //[object Set]
console.log(set instanceof Set); //true
console.log(set.constructor === Set); //true
console.log(Set.prototype.isPrototypeOf(set)); //当前对象是否为另外一个对象的原型,如果是就返回 true,否则就返回 false。
function isObject(value) {
const valueType = typeof value;
return (
value !== null && (valueType == "object" || valueType == "function")
);
}
const set = new Set(["abc", "cba", "nba"]);
const info = {
name: "why",
age: 18,
friend: {
name: "kobe",
address: {
name: "洛杉矶",
detail: "斯坦普斯中心",
},
},
set: set,
};
function deepCopy(originValue) {
if (!isObject(originValue)) {
return originValue;
}
if (originValue instanceof Set) {
const newObj = new Set();
for (const setItem of originValue) {
newObj.add(deepCopy(setItem));
}
return newObj;
}
const newObj = Array.isArray(originValue) ? [] : {};
for (const key in originValue) {
newObj[key] = deepCopy(originValue[key]);
}
return newObj;
}
console.log(deepCopy(info));
2. 值是function的时候
const info = {
name: "why",
age: 18,
friend: {
name: "kobe",
address: {
name: "洛杉矶",
detail: "斯坦普斯中心",
},
},
set: set,
running: function () {
console.log("running");
},
};
//如果不对function做特殊处理,深拷贝出来的function也是个空对象 running: {}
//我们深拷贝的时候,不需要对函数做深拷贝,没有必要创建新的函数,因为函数是用来执行的,再做深拷贝,对内存来说是浪费性能的
解决:
function isObject(value) {
const valueType = typeof value;
return (
value !== null && (valueType == "object" || valueType == "function")
);
}
const set = new Set(["abc", "cba", "nba"]);
const info = {
name: "why",
age: 18,
friend: {
name: "kobe",
address: {
name: "洛杉矶",
detail: "斯坦普斯中心",
},
},
set: set,
running: function () {
console.log("running");
},
};
function deepCopy(originValue) {
// 1.如果是原始类型, 直接返回
if (!isObject(originValue)) {
return originValue;
}
// 2.如果是set类型
if (originValue instanceof Set) {
const newObj = new Set();
for (const setItem of originValue) {
newObj.add(deepCopy(setItem));
}
return newObj;
}
//3.如果是函数类型,直接把函数返回出去,不进行深拷贝
if (typeof originValue === "function") {
return originValue;
}
// 4.如果是对象类型
const newObj = Array.isArray(originValue) ? [] : {};
for (const key in originValue) {
newObj[key] = deepCopy(originValue[key]);
}
return newObj;
}
console.log(deepCopy(info));
3. 值是Symbol类型的时候
问题点:
function isObject(value) {
const valueType = typeof value;
return (
value !== null && (valueType == "object" || valueType == "function")
);
}
const set = new Set(["abc", "cba", "nba"]);
const s1 = Symbol("s1");
const s2 = Symbol("s2");
const info = {
name: "why",
age: 18,
friend: {
name: "kobe",
address: {
name: "洛杉矶",
detail: "斯坦普斯中心",
},
},
// 1.特殊类型: Set
set: set,
// 2.特性类型: function
running: function () {
console.log("running~");
},
// 3.值的特殊类型: Symbol
s1:s1,
s2:s2
};
function deepCopy(originValue) {
// 1.如果是原始类型, 直接返回
if (!isObject(originValue)) {
return originValue;
}
// 2.如果是set类型
if (originValue instanceof Set) {
const newObj = new Set();
for (const setItem of originValue) {
newObj.add(deepCopy(setItem));
}
return newObj;
}
//3.如果是函数类型,直接把函数返回出去,不进行深拷贝
if (typeof originValue === "function") {
return originValue;
}
// 4.如果是对象类型
const newObj = Array.isArray(originValue) ? [] : {};
for (const key in originValue) {
newObj[key] = deepCopy(originValue[key]);
}
return newObj;
}
const newObj = deepCopy(info);
console.log(newObj);
console.log(newObj.s1 === info.s1); //true
console.log(newObj.s2 === info.s2); //true
//因为symbol是typeof info[s1] 是symbol 普通数据类型,isObject是false, 直接就返回了。所以我们需要再isObject之前判断是不是symbol类型,并且创建symbol类型进行返回。
解决:
function isObject(value) {
const valueType = typeof value;
return (
value !== null && (valueType == "object" || valueType == "function")
);
}
const set = new Set(["abc", "cba", "nba"]);
const s1 = Symbol("s1");
const s2 = Symbol("s2");
const info = {
name: "why",
age: 18,
friend: {
name: "kobe",
address: {
name: "洛杉矶",
detail: "斯坦普斯中心",
},
},
// 1.特殊类型: Set
set: set,
// 2.特性类型: function
running: function () {
console.log("running~");
},
// 3.值的特殊类型: Symbol
s1: s1,
s2: s2,
};
function deepCopy(originValue) {
// 0.如果是symbole类型, 返回symbol类型
if (typeof originValue === "symbol") {
return Symbol(originValue.description);
}
// 1.如果是原始类型, 直接返回
if (!isObject(originValue)) {
return originValue;
}
// 2.如果是set类型
if (originValue instanceof Set) {
const newObj = new Set();
for (const setItem of originValue) {
newObj.add(deepCopy(setItem));
}
return newObj;
}
//3.如果是函数类型,直接把函数返回出去,不进行深拷贝
if (typeof originValue === "function") {
return originValue;
}
// 4.如果是对象类型
const newObj = Array.isArray(originValue) ? [] : {};
for (const key in originValue) {
newObj[key] = deepCopy(originValue[key]);
}
return newObj;
}
const newObj = deepCopy(info);
console.log(newObj);
console.log(newObj.s1 === info.s1); //false
console.log(newObj.s2 === info.s2); //false
4. key是Symbol类型的时候
问题
const set = new Set(["abc", "cba", "nba"]);
const s1 = Symbol("s1");
const s2 = Symbol("s2");
const keyName = Symbol("keyname");
const info = {
name: "why",
age: 18,
friend: {
name: "kobe",
address: {
name: "洛杉矶",
detail: "斯坦普斯中心",
},
},
// 1.特殊类型: Set
set: set,
// 2.特性类型: function
running: function () {
console.log("running~");
},
// 3.值的特殊类型: Symbol
s1: s1,
s2: s2,
// 4.key是symbol时
[keyName]: "myName",
};
//深拷贝的结果就是压根没有这个symbole的键名字,因为for in 的时候,是不会遍历出来[keyName]的
//symbole的这种key是需要Object.getOwnPropertySymbols(obj)来获取到一个数组,然后再遍历这个数组,得到对应的值
解决
function isObject(value) {
const valueType = typeof value;
return (
value !== null && (valueType == "object" || valueType == "function")
);
}
const set = new Set(["abc", "cba", "nba"]);
const s1 = Symbol("s1");
const s2 = Symbol("s2");
const keyName = Symbol("keyname");
const info = {
name: "why",
age: 18,
friend: {
name: "kobe",
address: {
name: "洛杉矶",
detail: "斯坦普斯中心",
},
},
// 1.特殊类型: Set
set: set,
// 2.特性类型: function
running: function () {
console.log("running~");
},
// 3.值的特殊类型: Symbol
s1: s1,
s2: s2,
// 4.key是symbol时
[keyName]: "myName",
};
function deepCopy(originValue) {
// 0.如果是symbole类型, 返回symbol类型
if (typeof originValue === "symbol") {
return Symbol(originValue.description);
}
// 1.如果是原始类型, 直接返回
if (!isObject(originValue)) {
return originValue;
}
// 2.如果是set类型
if (originValue instanceof Set) {
const newObj = new Set();
for (const setItem of originValue) {
newObj.add(deepCopy(setItem));
}
return newObj;
}
//3.如果是函数类型,直接把函数返回出去,不进行深拷贝
if (typeof originValue === "function") {
return originValue;
}
// 4.如果是对象类型
const newObj = Array.isArray(originValue) ? [] : {};
// 遍历普通的key
for (const key in originValue) {
newObj[key] = deepCopy(originValue[key]);
}
// 单独遍历symbol
const symbolKeys = Object.getOwnPropertySymbols(originValue);
for (const symbolKey of symbolKeys) {
newObj[Symbol(symbolKey.description)] = deepCopy(
originValue[symbolKey]
);
}
return newObj;
}
const newObj = deepCopy(info);
console.log(newObj);
3.4. 手写深拷贝(循环引用)
问题:
const info = {
name: "why",
age: 18,
friend: {
name: "kobe",
address: {
name: "洛杉矶",
detail: "斯坦普斯中心",
},
},
//self:info这么写会报错,所以得写在外面
};
info.self = info;
//如果使用以前的deepCopy的函数的化,当遍历到self的时候,又会把info对象传进去,然后进行深拷贝,然后遍历self的时候,又会把info对象传进去。。。造成无限递归,栈内存耗尽
//test.html:34 Uncaught RangeError: Maximum call stack size exceeded (栈溢出 ) 浏览器报错
解决方法:
//当遍历到self的时候,又会再创建一个对象{}
/*但是这个对象就是第一次进入函数的时候创建的newObj对象,我们把这个值给遍历到self的时候,而不是再重新创建出一个对象*/
// 4.如果是对象类型
const newObj = Array.isArray(originValue) ? [] : {};
// 遍历普通的key
for (const key in originValue) {
newObj[key] = deepCopy(originValue[key]);
//newObj[slef] = newObj
}
info.self=info
那么第一次info传进deepCope的时候,会创建一个newObj
const map = new Map()
map.set(originValue,newObj)
if(map.has(originValue)){
return map.get(originValue)
}
当 遍历到self的时候,
他就需要deepCopy(originValue[key]) 也就是deepCopy(originValue)
这个时候就不去deepCope了,就从map里面拿地址就可以了,不再需要进行深拷贝了。
解决方法1:
function isObject(value) {
const valueType = typeof value;
return (
value !== null && (valueType == "object" || valueType == "function")
);
}
const info = {
name: "why",
age: 18,
friend: {
name: "kobe",
address: {
name: "洛杉矶",
detail: "斯坦普斯中心",
},
},
//self:info这么写会报错,所以得写在外面
};
info.self = info;
const map = new Map();
function deepCopy(originValue) {
// 0.如果是symbole类型, 返回symbol类型
if (typeof originValue === "symbol") {
return Symbol(originValue.description);
}
// 1.如果是原始类型, 直接返回
if (!isObject(originValue)) {
return originValue;
}
// 2.如果是set类型
if (originValue instanceof Set) {
const newObj = new Set();
for (const setItem of originValue) {
newObj.add(deepCopy(setItem));
}
return newObj;
}
//3.如果是函数类型,直接把函数返回出去,不进行深拷贝
if (typeof originValue === "function") {
return originValue;
}
// 4.如果是对象类型
if (map.get(originValue)) {
//如果已经存在originValue这个对象,就不再进行深拷贝,而是直接将哪个对象的地址返回出去。
return map.get(originValue);
}
const newObj = Array.isArray(originValue) ? [] : {};
//每次有新对象,就设置一次这个新对象的地址。
map.set(originValue, newObj);
// 遍历普通的key
for (const key in originValue) {
newObj[key] = deepCopy(originValue[key]);
}
// 单独遍历symbol
const symbolKeys = Object.getOwnPropertySymbols(originValue);
for (const symbolKey of symbolKeys) {
newObj[Symbol(symbolKey.description)] = deepCopy(
originValue[symbolKey]
);
}
return newObj;
}
const newObj = deepCopy(info);
console.log(newObj);
console.log(newObj === newObj.self); //true
map = null;
/*
1. 在我们想使用深拷贝函数,使用完后,这个map就没有用了,应该销毁掉,我们就map=null ,然后每次使用深拷贝,先 const map = new Map();,然后使用完后map=null,没有必要。而且如果不销毁,每次使用,往map里面积攒的东西就很多,多有遍历过的对象和它的地址都在里面。内存不友好
2.map是强引用
就算我们把
const newObj = deepCopy(info);
中的newObj = null,如果忘了把map=null
因为map是强引用,这个newObj也是不会销毁的。
*/
优化:通过参数传进来一个weakmap
function isObject(value) {
const valueType = typeof value;
return (
value !== null && (valueType == "object" || valueType == "function")
);
}
const info = {
name: "why",
age: 18,
friend: {
name: "kobe",
address: {
name: "洛杉矶",
detail: "斯坦普斯中心",
},
},
//self:info这么写会报错,所以得写在外面
};
info.self = info;
function deepCopy(originValue, map = new WeakMap()) {
// 0.如果是symbole类型, 返回symbol类型
if (typeof originValue === "symbol") {
return Symbol(originValue.description);
}
// 1.如果是原始类型, 直接返回
if (!isObject(originValue)) {
return originValue;
}
// 2.如果是set类型
if (originValue instanceof Set) {
const newObj = new Set();
for (const setItem of originValue) {
newObj.add(deepCopy(setItem));
}
return newObj;
}
//3.如果是函数类型,直接把函数返回出去,不进行深拷贝
if (typeof originValue === "function") {
return originValue;
}
// 4.如果是对象类型
if (map.get(originValue)) {
//如果已经存在originValue这个对象,就不再进行深拷贝,而是直接将哪个对象的地址返回出去。
return map.get(originValue);
}
const newObj = Array.isArray(originValue) ? [] : {};
//每次有新对象,就设置一次这个新对象的地址。
map.set(originValue, newObj);
// 遍历普通的key
for (const key in originValue) {
newObj[key] = deepCopy(originValue[key], map);
}
// 单独遍历symbol
const symbolKeys = Object.getOwnPropertySymbols(originValue);
for (const symbolKey of symbolKeys) {
newObj[Symbol(symbolKey.description)] = deepCopy(
originValue[symbolKey]
);
}
return newObj;
}
const newObj = deepCopy(info);
console.log(newObj);
console.log(newObj === newObj.self); //true









网友评论