var a = { a: 'a' } var b = a b.a = 'b' a.a // 'b'
由于js中的对象(引用数据类型)保存在堆中,而多个变量引用同一块堆中的内容时,其中一个修改,则会影响全部引用改内容的变量;有时候咱们不但愿这样es6
浅拷贝,对目标对象进行遍历,而后将其属性逐一拷贝至新建对象中数组
function shallowCopy(source) { const obj = {} for (let i in source) { obj[i] = source[i] } return obj }
测试一下函数
var obj = { a: 1, b: [1, 2, 3], c: {c1: 4} } var obj1 = shallowCopy(obj) obj1.a = 11 obj1.b[0] = 11 obj1.c.c1 = 44 obj.a // 1 obj.b[0] // 11 obj.c.c1 // 44
测试结果
浅拷贝适用于对象中全部属性都为基础数据类型时,若是源对象包含有引用类型的属性,则浅拷贝没法切断与源对象的关系
es6提供了Object.assign,功能与之相似
还有展开运算符: var obj1 = {...obj}性能
针对方法一的问题,提出新的方法:
利用JSON.stringify + JSON.parse 进行对象的序列化/反序列化进行对象的拷贝测试
var obj1 = JSON.parse(JSON.stringify(obj)) obj1.a = 11 obj1.b[0] = 11 obj1.c.c1 = 44 obj.a // 1 obj.b[0] // 1 obj.c.c1 // 4
方法二彷佛完美得解决了方法一遗留的问题,再进行测试优化
function Person(age) { this.age = age } var p = new Person(3) var obj = { a: new Date(), b: /abc/igmuy, c() {}, d: new Array(2), e: p, } var obj1 = JSON.parse(JSON.stringify(obj)) obj1.a // '2019-12-18T08:40:21.650Z' 实际上和调用Date对象的toISOString方法一致,返回的为时间字符串 obj1.b // {} RegExp对象不会被正确拷贝 obj1.c // undefined 函数不会被正确拷贝 obj1.d // [null, null] 数组空位不会被正确拷贝 obj1.e // obj1.e.constructor指向Object,即原型丢失
还有一种状况,包含循环引用过的对象使用JSON序列化会抛出异常this
var obj = {} obj.a = obj var obj1 = JSON.parse(JSON.stringify(obj)) // 结果会抛出异常
使用JSON序列化/反序列化实现拷贝的局限性prototype
以上几点虽然是该方法的局限,但一般该方法能知足大多数场景,也不失为一个简便的方法code
针对方法一没法对引用类型属性进行拷贝和方法二存在的局限,能够针对不一样状况,进行针对性处理regexp
// 类型判断方法 const isType = (source, type) => Object.prototype.toString.call(source).slice(8, -1).toLowerCase() === type.toLowerCase() const deepClone = source => { const sources = [] const children = [] const _deep = source => { if (typeof source !== 'object' || isType(source, 'null') || isType(source, 'undefined') ) {return source} let child; if (isType(source, 'date')) { // 处理date类型 child = new Date(source.getTime()) } else if (isType(source, 'regexp')) { // 处理正则 child = new RegExp(source.source, source.flags) child.lastIndex = source.lastIndex } else if (isType(source, 'array')) { // 处理数组 child = [] } else { const proto = Object.getPrototypeOf(source) child = Object.create(proto) } // 处理循环引用 const index = sources.indexOf(source) if (index >= 0) { return children[index] } sources.push(source) children.push(child) for (let i in source) { child[i] = _deep(source[i]) } return child } return _deep(source) }
该方法解决了前两个方法中存在的一些问题
虽然方法三解决了一些问题,但同时引入了一些问题: 因为使用了遍历递归的方法,在性能方面会有一些问题,有没有办法优化呢?
。。。