ECMAScript 中數據類型可分爲:函數
不一樣類型的存儲方式:性能
不一樣類型的複製方式:ui
let foo = 1;
let bar = foo;
console.log(foo === bar); // -> true
// 修改foo變量的值並不會影響bar變量的值
let foo = 233;
console.log(foo); // -> 233
console.log(bar); // -> 1
複製代碼
let foo = {
name: 'leeper',
age: 20
}
let bar = foo;
console.log(foo === bar); // -> true
// 改變foo變量的值會影響bar變量的值
foo.age = 19;
console.log(foo); // -> {name: 'leeper', age: 19}
console.log(bar); // -> {name: 'leeper', age: 19}
複製代碼
首先深複製和淺複製只針對像 Object, Array 這樣的複雜對象的。簡單來講,淺複製只複製一層對象的屬性,而深複製則遞歸複製了全部層級。spa
// 使用Object.assign解決
// 使用Object.assign(),你就能夠沒有繼承就能得到另外一個對象的全部屬性,快捷好用。
// Object.assign 方法只複製源對象中可枚舉的屬性和對象自身的屬性。
let obj = { a:1, arr:[2,3]};
let res = Object.assign({}, obj)
console.log(res.arr === obj.arr); // true,指向同一個引用
console.log(res === obj); // false
複製代碼
// 使用擴展運算符(…)來解決
let obj = { a:1, arr:[2,3]};
let res = {...obj};
console.log(res.arr === obj.arr); // true,指向同一個引用
console.log(res === obj); // false
複製代碼
const shallowCopy = (sourceObj) => {
if (typeof sourceObj !== 'object') return;
let newObj = sourceObj instanceof Array ? [] : {};
for(let key in sourceObj){
if(sourceObj.hasOwnProperty(key)) {
//只複製元素自身的屬性,不復制原型鏈上的
newObj[key] = sourceObj[key];
}
}
return newObj;
}
let obj = { a:1, arr:[2,3]};
let res = shallowCopy(obj);
console.log(res.arr === obj.arr); // true,指向同一個引用
console.log(res.a === obj.a); // false
複製代碼
由於淺複製只會將對象的各個屬性進行依次複製,並不會進行遞歸複製,而 JavaScript 存儲對象都是存地址的,因此淺複製會致使 obj.arr 和 shallowObj.arr 指向同一塊內存地址,大概的示意圖以下。prototype
// 能夠經過 JSON.parse(JSON.stringify(object)) 來解決
let a = {
age: 1,
jobs: {
first: 'FE'
}
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE
複製代碼
可是該方法也是有侷限性的:指針
而且該函數是內置函數中處理深拷貝性能最快的。固然若是你的數據中含有以上三種狀況下,可使用 lodash 的深拷貝函數。code
const deepCopy = (sourceObj) => {
if(typeof sourceObj !== 'object') return;
let newObj = sourceObj instanceof Array ? [] : {};
for(let key in sourceObj){
if(sourceObj.hasOwnProperty(key)) {
//只複製元素自身的屬性,不復制原型鏈上的
newObj[key] = (typeof sourceObj[key] === 'object' ? deepCopy(sourceObj[key]) : sourceObj[key]);
}
}
return newObj;
}
let obj = { a:1, arr:[2,3]};
let res = deepCopy(obj);
console.log(res.arr === obj.arr); // false,指向不一樣的引用
console.log(res === obj); // false
複製代碼
而深複製則不一樣,它不只將原對象的各個屬性逐個複製出去,並且將原對象各個屬性所包含的對象也依次採用深複製的方法遞歸複製到新對象上。這就不會存在上面 obj 和 shallowObj 的 arr 屬性指向同一個對象的問題。cdn
let a = [1, 2, 3, 4];
let b = a.slice();
console.log(a === b); // -> false(當引用類型時須要知足值相等和引用相等才爲 true)
a[0] = 5;
console.log(a); // -> [5, 2, 3, 4]
console.log(b); // -> [1, 2, 3, 4]
複製代碼
let a = [1, 2, 3, 4];
let b = a.concat();
console.log(a === b); // -> false
a[0] = 5;
console.log(a); // -> [5, 2, 3, 4]
console.log(b); // -> [1, 2, 3, 4]
複製代碼
看起來 Array 的 slice(), concat() 彷佛是深拷貝,再接着看就知道它們到底是深拷貝仍是淺拷貝:對象
let a = [[1, 2], 3, 4];
let b = a.slice();
console.log(a === b); // -> false
a[0][0] = 0;
console.log(a); // -> [[0, 2], 3, 4]
console.log(b); // -> [[0, 2], 3, 4]
複製代碼
一樣,對於concat()也進行驗證:blog

let a = [[1, 2], 3, 4];
let b = a.concat();
console.log(a === b); // -> false
a[0][0] = 0;
console.log(a); // -> [[0, 2], 3, 4]
console.log(b); // -> [[0, 2], 3, 4]
複製代碼
綜上, Array 的 slice 和 concat 方法並非真正的深拷貝,對於 Array 的第一層的元素是深拷貝,而 Array 的第二層 slice 和 concat 方法是複製引用。因此,Array 的 slice 和 concat 方法都是淺拷貝。