原生js的对象拷贝都是浅拷贝,若是须要深拷贝则要手动作一些操做json
例子:数组
let obj = { name: 'lizitao', msg: { age: '2' } }; let cp = JSON.parse(JSON.stringify(obj)); console.log(cp);
优势: 写起来很是简单
缺点:局限性太多了。由于只支持json所支持的那些数据类型。像函数、undefined、正则等这些都是不支持的。遇到环状结构则会报错:app
var obj = { a: undefined }; console.log(JSON.parse(JSON.stringify(obj))) // {} // 属性a被自动忽略了
JSON.stringify则会检测是否存在环,存在则抛出错误函数
var obj = {}; obj.child = obj; JSON.stringify(a) // Uncaught TypeError: Converting circular structure to JSON
因此咱们只能复杂一点的方式,用递归来实现深拷贝this
const clone = function(source){ if(source instanceof Object){ let result = {} for (const key in source) { result[key] = clone(source[key]) } return result } else { return source } }
先判断是否为对象,非对象则直接返回它的值。若是是对象则新建一个空对象 {},并复制属性到该对象上。注意属性的复制是递归调用clone函数的:result[key] = clone(source[key])
,这样才能保证每一层都是深拷贝!。
不过它只判断了是否为对象,并假设对象都是像{name: value}的形式。若是对象是数组、函数、日期等等就不能正常运行了。因此要判断对象究竟是哪一种类型的对象,并根据对象具体类型来初始化result,用这样的形式:code
if(source instanceof Object){ //... if(source instanceof Array){ //... } else if(source instanceof Function){ //... } else if(source instanceof Date){ //... } // 省略许多else if, 判断越详细那么支持的对象类型就越多 else { //... } } else { //... }
因而咱们按照这个思路写出第二版对象
const clone = function(source){ if(source instanceof Object){ let result = null if(source instanceof Array){ result = [] } else if(source instanceof Function){ result = function(){ return source.apply(this, arguments) } } else if (source instanceof Date) { result = new Date(source); } else { result = {} } for (const key in source) { result[key] = this.clone(source[key]) } return result } else { return source } }
最后要解决的是对象是环的状况。基本思路是用一个数组来存储全部复制过的对象,每次调用前先判断对象是否复制过了,若是复制过就直接返回。递归