JavaScript 对象复制这件事之前直接 copy 的 stackoverflow 上的方法: JSON.parse(JSON.stringify(food))
。最近看了几篇文章, 总结抄袭了一下。
首先,JavaScript 的对象是引用类型,所以我们没法直接用=
来进行 copy。目前常用的有这么三种:
const vehicle = { bike: 🚲, ufo: 🛸 };
// spread操作符
const vehicle1 = { ...vehicle };
// Object.prototyp.assign
const vehicle2 = Object.assign({}, vehicle);
// "JSON"
const vehicle3 = JSON.parse(JSON.stringify(vehicle));
console.log(vehicle1, vehicle2, vehicle3);
对象是引用类型
你可能首先想问,为什么不能直接用=
?我们来看下如果我们这么做,会发生什么:
const obj = { one: 1, two: 2 };
const obj2 = obj;
console.log(obj, obj2);
// {one: 1, two: 2}
// {one: 1, two: 2}
目前这两个对象的输出看起来是一样的。我们看下如果编辑第二个对象会发生什么?
const obj2.three = 3;
console.log(obj2);
// {one: 1, two: 2, three: 3}; <-- ✔
console.log(obj);
// {one: 1, two: 2, three: 3}; <-- 😱
啥情况?我们明明只改了obj2
为什么obj
也被改了?这是因为 Objects 是引用类型。所以当你使用=
,它会拷贝指向这块内存地址的指针。引用类型不持有值本身,他们只是一个指向内存空间中这个值的指针。
1. 使用展开运算符
使用展开运算符会克隆你的对象。不过这只是浅拷贝。
const food = { beef: "🥩", bacon: "🥓" };
const cloneFood = { ...food };
console.log(cloneFood);
// { beef: '🥩', bacon: '🥓' }
2. 使用 Object.assign
或者用Object.assign
方法,这个也是用来生成一个对象的浅拷贝对象。
const food = { beef: "🥩", bacon: "🥓" };
const cloneFood = Object.assign({}, food);
console.log(cloneFood);
// { beef: '🥩', bacon: '🥓' }
需要注意的是{}
作为第一个参数传进去, 这样做会确保你不会更改原有的对象。
3. 使用 JSON
这个最终的办法会让你获得一个深拷贝对象。正像我之前提到的,这样做只是一个“便捷”并且不那么“完美”的办法去深拷贝一个对象。如果要更完美的方案,我推荐lodash
。
const food = { beef: "🥩", bacon: "🥓" };
const cloneFood = JSON.parse(JSON.stringify(food));
console.log(cloneFood);
// { beef: '🥩', bacon: '🥓' }
Lodash DeepClone vs JSON
deepClone 和 JSON.stringify/parse 的区别:
-
JSON.stringify/parse只作用于 Number 和 String 以及没有 function 和 Symbol 属性的 Object。
-
deepClone可以作用于所有的类型, function 和 Symbol 被作为引用拷贝过去。
这有一个例子
const lodashClonedeep = require("lodash.clonedeep");
const arrOfFunction = [
() => 2,
{
test: () => 3
},
Symbol("4")
];
// deepClone copy by refence function and Symbol
console.log(lodashClonedeep(arrOfFunction));
// JSON replace function with null and function in object with undefined
console.log(JSON.parse(JSON.stringify(arrOfFunction)));
// function and symbol are copied by reference in deepClone
console.log(
lodashClonedeep(arrOfFunction)[0] === lodashClonedeep(arrOfFunction)[0]
);
console.log(
lodashClonedeep(arrOfFunction)[2] === lodashClonedeep(arrOfFunction)[2]
);
深拷贝 vs 浅拷贝
当使用...
copy 对象的时候, 只是创建一个浅拷贝。如果一个数组是嵌套的或者多维的, 这个是搞不定的。下边是例子:
const nestedObject = {
country: 'US',
{
city: 'New York'
}
}
浅拷贝
我们使用展开运算符来克隆一个对象试一下:
const shallowClone = { ...nestedObject };
// Changed our cloned object
clonedNestedObject.country = "CA";
clonedNestedObject.country.city = "Toronto";
上边代码里我们改变了对象的 city 属性,看一下输出:
console.log(shallowClone);
// {country: 'CA', {city: 'Toronto'}} <-- ✅
console.log(nestedObject);
// {country: 'US', {city: 'Toronto'}} <-- 😱
这个浅拷贝的例子表明了, 对象的第一级属性是被拷贝了,但是更深一层的都只是引用。
深拷贝
现在看一个用 JSON 来实现深拷贝的例子:
const deepClone = JSON.parse(JSON.stringify(nestedObject));
console.log(deepClone);
// {country: 'CA', {city: 'Toronto'}} <-- ✅
console.log(nestedObject);
// {country: 'US', {city: 'New York'}} <-- ✅
就像例子里显示的那样,深拷贝可以真正的拷贝嵌套对象。
性能
暂时只说结果,Object.assign
性能要远好于JSON
。
Last modified on 2020-03-01