深浅拷贝

对象的赋值操作其实是对地址的复制,而拷贝就是创建一个新对象然后把原对象的属性复制到这个新对象中。

多个变量指向同一个对象,会导致牵一发而动全身的情况出现。

var a = { age: 1}
var b = a
b.age = 2
console.log(a.age) // 2

浅拷贝

把对象中的属性复制到新对象中,但是如果这个属性是对象,复制的是地址。

实现很简单,就是遍历对象,然后把对象自身的属性和属性值都对应的放在新对象上。

function shallowClone(obj) {
// 如果传入参数的不是对象,就返回传入的参数
if(typeof obj !=='object' || obj === null) return obj
// obj 是数组的话,返回的新对象也应是数组
var newObj = Array.isArray(obj) ? [] : {}
for(var key in obj) {
if(obj.hasOwnProperty(key)) {
newObj[key] = obj[key]
}
}
return newObj
}

var a = {age: 1, foo:{x:100}}
var b = shallowClone(a)

a.age = 2
a.foo.x = 200
console.log(b.age) // 1
console.log(b.foo) // {x: 200}

常见的浅拷贝

拷贝数组

  • slice()

    var arr = ['old', 1, 2, 3]
    var newArr = arr.slice()
    newArr[0] = 'new'
    console.log(arr) // ["old", 1, 2, 3]
    console.log(newArr) // ["new", 1, 2, 3]
  • concat()

    var newArr = arr.concat()

Object.assign() 浅拷贝对象

Object.assing(target, [obj1,obj2...])将所有可枚举属性的值从一个或多个源对象复制到目标对象中,返回目标对象

var a = {name: '张三'}
var b = {age: 20}

var p = Object.assign({}, a, b)
console.log(p) // {name: "张三", age: 20}
a.name = '李四'
b.age = 30
console.log(p) // {name: "张三", age: 20}

展开运算符进行浅拷贝

var a = {age:1}
var b = {...a}
a.age = 2
console.log(b.age) // 1

深拷贝

把对象中的属性复制到新对象中,但是如果这个属性是对象,复制的是是对象而不是地址。

拷贝的时候判断一下属性值的类型,如果是对象,递归调用深拷贝函数

function deepClone(obj) {
if(!isObj(obj)) throw new Error('非对象')
var newObj = Array.isArray(obj) ? [] : {}
for( var key in obj) {
if(obj.hasOwnProperty(key)) {
newObj[key] = isObj(obj[key]) ? deepClone(obj[key]) : obj[key]
}
}

return newObj

function isObj(o) {
return (typeof o === 'object' || typeof o === 'function') && o !== null
}
}


var a = {age: 1, foo:{x:100}}
var b = deepClone(a)

a.age = 2
a.foo.x = 200
console.log(b.age) // 1
console.log(b.foo) // {x: 100}

常见深拷贝

先序列化成字符串再反序列化成为新对象

JSON.parse(JSON.stringify(object)) 这个方法简单粗暴,大多数情况下适用

var p = {
name: '张三',
age: 20,
jobs: {
name: '程序猿',
city: '北京'
}
}

var p2 = JSON.parse(JSON.stringify(p))
p2.jobs.city = '深圳'
console.log(p.jobs.city) // 北京

但是该方法也是有局限性的:

  • 忽略 undefined
  • 忽略 symbol
  • 忽略函数
  • 包含循环引用的对象执行 JSON.stringify() 会出错
var bar = {
a: undefined,
b: Symbol('bb'),
c: function(){console.log('this is a')},
d: 'd'
}

var bar2 = JSON.parse(JSON.stringify(bar))
console.log(bar2) // {d: "d"}

函数库lodash

该函数库提供了 cloneDeep 方法实现深拷贝

深拷贝实现其实很困难的,需要考虑很多边界问题,比如原型链如何处理、Dom 元素的处理。实际开发者如果确定要用深拷贝,可以使用 lodash

var _ = require('lodash')
var obj = {
a: 1,
b: {foo: function()}
}
var obj2 = _.cloneDeep(obj)

总结

  • 浅拷贝就是在拷贝对象属性时,属性值也是对象的话,拷贝的只是引用,而深拷贝拷贝的是对象。
  • 深拷贝会用到递归,效率低下,能不用则不用。
© 2019 墨夜 All Rights Reserved.
Theme by hiero