ECMAScript 中数据类型可分为:markdown
不一样类型的存储方式:函数
不一样类型的复制方式:性能
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 属性指向同一个对象的问题。orm
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() 彷佛是深拷贝,再接着看就知道它们到底是深拷贝仍是浅拷贝:cdn
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()也进行验证:对象
 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 方法都是浅拷贝。