实现 js 中所有对象的深拷贝(包装对象,Date 对象,正则对象)

通过递归可以简单实现对象的深拷贝,但是这种方法不管是 ES6 还是 ES5 实现,都有同样的缺陷,就是只能实现特定的 object 的深度复制(比如数组和函数),不能实现包装对象 Number,String , Boolean,以及 Date 对象,RegExp 对象的复制。

(1)简单的深拷贝

function deepClone(obj) {
  var newObj = obj instanceof Array ? [] : {}

  for (var i in obj) {
    newObj[i] = typeof obj[i] == 'object' ? deepClone(obj[i]) : obj[i]
  }

  return newObj
}

这种方法可以实现一般对象和数组对象的拷贝,比如:

var arr = [1, 2, 3]

var newArr = deepClone(arr)

// newArr->[1,2,3]

var obj = {
  x: 1,
  y: 2
}

var newObj = deepClone(obj)

// newObj={x:1,y:2}

但是不能实现例如包装对象 Number,String,Boolean,以及正则对象 RegExp 和 Date 对象的拷贝,比如:

//Number 包装对象

var num = new Number(1)

typeof num // "object"

var newNum = deepClone(num)

//newNum -> {} 空对象

//String 包装对象

var str = new String('hello')

typeof str //"object"

var newStr = deepClone(str)

//newStr-> {0:'h',1:'e',2:'l',3:'l',4:'o'};

//Boolean 包装对象

var bol = new Boolean(true)

typeof bol //"object"

var newBol = deepClone(bol)

// newBol ->{} 空对象

(2)valueof()函数

所有对象都有 valueOf 方法,valueOf 方法对于:如果存在任意原始值,它就默认将对象转换为表示它的原始值。对象是复合值,而且大多数对象无法真正表示为一个原始值, 因此默认的 valueOf()方法简单地返回对象本身,而不是返回一个原始值。数组、函数和正则表达式简单地继承了这个默认方法,调用这些类型的实例的 valueOf()方法只是简单返回这个对象本身。

对于原始值或者包装类:

function baseClone(base) {
  return base.valueOf()
}

//Number

var num = new Number(1)

var newNum = baseClone(num)

//newNum->1

//String

var str = new String('hello')

var newStr = baseClone(str)

// newStr->"hello"

//Boolean

var bol = new Boolean(true)

var newBol = baseClone(bol)

//newBol-> true

其实对于包装类,完全可以用=号来进行拷贝,其实没有深拷贝一说,这里用 valueOf 实现,语法上比较符合规范。

对于 Date 类型:

因为 valueOf 方法,日期类定义的 valueOf()方法会返回它的一个内部表示:1970 年 1 月 1 日以来的毫秒数.因此我们可以在 Date 的原型上定义拷贝的方法:

Date.prototype.clone = function () {
  return new Date(this.valueOf())
}

var date = new Date('2010')

var newDate = date.clone()

// newDate-> Fri Jan 01 2010 08:00:00 GMT+0800

对于正则对象 RegExp:

RegExp.prototype.clone = function () {
  var pattern = this.valueOf()

  var flags = ''

  flags += pattern.global ? 'g' : ''

  flags += pattern.ignoreCase ? 'i' : ''

  flags += pattern.multiline ? 'm' : ''

  return new RegExp(pattern.source, flags)
}

var reg = new RegExp('/111/')

var newReg = reg.clone()

//newReg-> /\/111\//

最终解决方案:

// 在深拷贝的基础上,对(包装对象Number,String,Boolean;Date 对象,RegExp正则对象)进行深拷贝
function deepClone(obj) {
  let newObj = obj instanceof Array ? [] : {}
  for (let i in obj) {
    // for...in 会遍历原型上的属性,此处只拷贝obj对象自身的属性
    if (obj.hasOwnProperty(i)) {
      let type = Object.prototype.toString.call(obj[i])
      if (typeof obj[i] == 'object') {
        // 拷贝的值为对象,则需要深拷贝
        if (type == '[object Date]') {
          newObj[i] = new Date(obj[i].valueOf())
        } else if (type == '[object RegExp]') {
          // 正则对象
          let pattern = obj[i].valueOf()
          let flags = ''
          flags += pattern.global ? 'g' : ''
          flags += pattern.ignoreCase ? 'i' : ''
          flags += pattern.multiline ? 'm' : ''
          newObj[i] = new RegExp(pattern.source, flags)
        } else if (type == '[object Array]' || type == '[object Object]') {
          // 数组或对象
          newObj[i] = deepClone(obj[i])
        } else {
          // 包装对象Number,String,Boolean
          newObj[i] = obj[i].valueOf()
        }
      } else if (typeof obj[i] == 'function') {
        // 函数
        newObj[i] = new Function('return ' + obj[i].toString())()
      } else {
        // 拷贝的值为原始值,则直接复制该值
        newObj[i] = obj[i]
      }
    }
  }
  return newObj
}

测试:

let obj = {
  name: 'zs',
  age: 20,
  food: ['apple', 'banana', 'orange'],
  obj1: {
    school: 'nyist'
  },
  reg: new RegExp('/A-Z/', 'gi'),
  date: new Date(),
  bol: new Boolean(true),
  num: new Number(999),
  fn: function () {
    this.age++
  }
}

let res = deepClone(obj)

res.obj1.school = '清华'
res.fn = function (a, b) {
  console.log(a, b)
}
res.food[1] = '香蕉'

console.log('res', res, 'obj', obj)
console.log(res.reg === obj.reg)
console.log(res.date === obj.date)

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_L...

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值