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


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

    (1)简单的深拷贝

    1. function deepClone(obj) {
    2.   var newObj = obj instanceof Array ? [] : {}
    3.  
    4.   for (var i in obj) {
    5.     newObj[i] = typeof obj[i] == 'object' ? deepClone(obj[i]) : obj[i]
    6.   }
    7.  
    8.   return newObj
    9. }


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

    1. var arr = [1, 2, 3]
    2.  
    3. var newArr = deepClone(arr)
    4.  
    5. // newArr->[1,2,3]
    6.  
    7. var obj = {
    8.   x: 1,
    9.   y: 2
    10. }
    11.  
    12. var newObj = deepClone(obj)
    13.  
    14. // newObj={x:1,y:2}


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

    1. //Number 包装对象
    2.  
    3. var num = new Number(1)
    4.  
    5. typeof num // "object"
    6.  
    7. var newNum = deepClone(num)
    8.  
    9. //newNum -> {} 空对象
    10.  
    11. //String 包装对象
    12.  
    13. var str = new String('hello')
    14.  
    15. typeof str //"object"
    16.  
    17. var newStr = deepClone(str)
    18.  
    19. //newStr-> {0:'h',1:'e',2:'l',3:'l',4:'o'};
    20.  
    21. //Boolean 包装对象
    22.  
    23. var bol = new Boolean(true)
    24.  
    25. typeof bol //"object"
    26.  
    27. var newBol = deepClone(bol)
    28.  
    29. // newBol ->{} 空对象


    (2)valueof()函数

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

    对于原始值或者包装类:

    1. function baseClone(base) {
    2.   return base.valueOf()
    3. }
    4.  
    5. //Number
    6.  
    7. var num = new Number(1)
    8.  
    9. var newNum = baseClone(num)
    10.  
    11. //newNum->1
    12.  
    13. //String
    14.  
    15. var str = new String('hello')
    16.  
    17. var newStr = baseClone(str)
    18.  
    19. // newStr->"hello"
    20.  
    21. //Boolean
    22.  
    23. var bol = new Boolean(true)
    24.  
    25. var newBol = baseClone(bol)
    26.  
    27. //newBol-> true


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

    对于 Date 类型:

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

    1. Date.prototype.clone = function () {
    2.   return new Date(this.valueOf())
    3. }
    4.  
    5. var date = new Date('2010')
    6.  
    7. var newDate = date.clone()
    8.  
    9. // newDate-> Fri Jan 01 2010 08:00:00 GMT+0800
    10. 对于正则对象 RegExp:
    11. RegExp.prototype.clone = function () {
    12.   var pattern = this.valueOf()
    13.  
    14.   var flags = ''
    15.  
    16.   flags += pattern.global ? 'g' : ''
    17.  
    18.   flags += pattern.ignoreCase ? 'i' : ''
    19.  
    20.   flags += pattern.multiline ? 'm' : ''
    21.  
    22.   return new RegExp(pattern.source, flags)
    23. }
    24.  
    25. var reg = new RegExp('/111/')
    26.  
    27. var newReg = reg.clone()
    28.  
    29. //newReg-> /\/111\//


    最终解决方案:

    // 在深拷贝的基础上,对(包装对象Number,String,Boolean;Date 对象,RegExp正则对象)进行深拷贝

    1. function deepClone(obj) {
    2.   let newObj = obj instanceof Array ? [] : {}
    3.   for (let i in obj) {
    4.     // for...in 会遍历原型上的属性,此处只拷贝obj对象自身的属性
    5.     if (obj.hasOwnProperty(i)) {
    6.       let type = Object.prototype.toString.call(obj[i])
    7.       if (typeof obj[i] == 'object') {
    8.         // 拷贝的值为对象,则需要深拷贝
    9.         if (type == '[object Date]') {
    10.           newObj[i] = new Date(obj[i].valueOf())
    11.         } else if (type == '[object RegExp]') {
    12.           // 正则对象
    13.           let pattern = obj[i].valueOf()
    14.           let flags = ''
    15.           flags += pattern.global ? 'g' : ''
    16.           flags += pattern.ignoreCase ? 'i' : ''
    17.           flags += pattern.multiline ? 'm' : ''
    18.           newObj[i] = new RegExp(pattern.source, flags)
    19.         } else if (type == '[object Array]' || type == '[object Object]') {
    20.           // 数组或对象
    21.           newObj[i] = deepClone(obj[i])
    22.         } else {
    23.           // 包装对象NumberStringBoolean
    24.           newObj[i] = obj[i].valueOf()
    25.         }
    26.       } else if (typeof obj[i] == 'function') {
    27.         // 函数
    28.         newObj[i] = new Function('return ' + obj[i].toString())()
    29.       } else {
    30.         // 拷贝的值为原始值,则直接复制该值
    31.         newObj[i] = obj[i]
    32.       }
    33.     }
    34.   }
    35.   return newObj
    36. }


    测试:

    1. let obj = {
    2. name: 'zs',
    3. age: 20,
    4. food: ['apple', 'banana', 'orange'],
    5. obj1: {
    6. school: 'nyist'
    7. },
    8. reg: new RegExp('/A-Z/', 'gi'),
    9. date: new Date(),
    10. bol: new Boolean(true),
    11. num: new Number(999),
    12. fn: function () {
    13. this.age++
    14. }
    15. }
    16. let res = deepClone(obj)
    17. res.obj1.school = '清华'
    18. res.fn = function (a, b) {
    19. console.log(a, b)
    20. }
    21. res.food[1] = '香蕉'
    22. console.log('res', res, 'obj', obj)
    23. console.log(res.reg === obj.reg)

    原文链接:https://blog.csdn.net/weixin_52624519/article/details/129265211

  • 相关阅读:
    疫情在家“闭关修炼”,读完这些Java技术栈,愿金九银过五斩六
    【漏洞复现】EnjoySCM存在文件上传漏洞
    某城商行两地三中心建设存储架构规划及方案验证实践
    面经pc端项目
    无涯教程-JavaScript - IMDIV函数
    大数据之Linux(一)
    web3 dapp React项目引入 antd 对 balance 用户token信息组件进行样式改造
    第十四届蓝桥杯模拟赛(第二期)——C语言版
    跨境电商如何通过打好数据底座,实现低成本稳步增长
    [附源码]计算机毕业设计springboot市场摊位管理系统
  • 原文地址:https://blog.csdn.net/qq_41328247/article/details/132736346