zoukankan      html  css  js  c++  java
  • JS对象的拷贝

    1:对数据进行备份的时候,如果这个数据是基本的数据类型,那么很好办,通过赋值实现复制即可。

    赋值与浅拷贝的区别
    var obj1 = {
        'name' : 'zhangsan',
        'age' :  '18',
        'language' : [1,[2,3],[4,5]],
    };
    
    var obj2 = obj1;  //赋值得到的对象
    
    
    var obj3 = shallowCopy(obj1);  //通过浅拷贝得到的对象
    function shallowCopy(src) {
        var dst = {};
        for (var prop in src) {
            if (src.hasOwnProperty(prop)) {
                dst[prop] = src[prop];
            }
        }
        return dst;
    }
    
    obj2.name = "lisi"; //影响obj1
    obj3.age = "20"; //不影响obj1
    
    obj2.language[1] = ["二","三"]; //影响obj1
    obj3.language[2] = ["四","五"]; //影响obj1
    
    console.log(obj1);  
    //obj1 = {
    //    'name' : 'lisi',
    //    'age' :  '18',
    //    'language' : [1,["二","三"],["四","五"]],
    //};
    
    console.log(obj2);
    //obj2 = {
    //    'name' : 'lisi',
    //    'age' :  '18',
    //    'language' : [1,["二","三"],["四","五"]],
    //};
    
    console.log(obj3);
    //obj3 = {
    //    'name' : 'zhangsan',
    //    'age' :  '20',
    //    'language' : [1,["二","三"],["四","五"]],
    //};
    
    
    注释:这是因为浅拷贝只复制一层对象的属性,并不包括对象里面的为引用类型的数据。所以就会出现改变浅拷贝得到的 obj3 中的引用类型时,会使原始数据得到改变
    深拷贝方法1:JSON.parse(JSON.stringify(obj))
    let obj1 = {
        name:'pan'
    }
    
    let obj2 = JSON.parse(JSON.stringify(obj1))
    
    obj2.name = 'rui'
    console.log(obj1)  //{name:pan}
    
    console.log(obj2)    //{name:rui}
    console.log(obj1 === obj2) //false
    //缺点:对象必须遵从JSON的格式
    let obj1 = {
      a: '1',
      b: '2',
      c: function func() {}
    }
    
    let obj4 = JSON.parse(JSON.stringify(obj1))
    
    console.log(obj4) //{ a: '1', b: '2' } 好像漏了些什么
    深拷贝方法2:Object.assign(target, …sources)
    //Object.assign() 方法用于将所有可枚举的属性的值从一个或多个源对象复制到目标对象。它将返回目标对象
    // 复制
    let c = Object.assign({}, { a: 'apple' });
    console.log(c); // { a: 'apple' }
    
    //合并
    let o = {}
    let c = Object.assign(o, { a: 'apple' }, { b: 'banana' }, { c: 'cake' } )
    console.log(c) // { a: 'apple', b: 'banana', c: 'cake' }
    
    //如果对象本身存在的属性会更新,不存在的属性会增加
    let o = {
      name:'pan'
    }
    let oo = {
      name:'rui',
      id:100
    }
    
    let c = Object.assign(o, oo);
    console.log(o);    //{name:'rui',id:100}
    console.log(oo);//{name:'rui',id:100}
    console.log(c);//{name:'rui',id:100}
    // 判断是否为对象
    function isObject(o) {
        return (typeof o === 'object' || typeof o === 'function') && o !== null
    }
    //测试用例需要的对象
    let test = {
        num: 0,
        str: '',
        boolean: true,
        unf: undefined,
        nul: null,
        obj: {
            name: '我是一个对象',
            id: 1
        },
        arr: [0, 1, 2],
        func: function() {
            console.log('我是一个函数')
        },
        date: new Date(0),
        reg: new RegExp('/我是一个正则/ig'),
        err: new Error('我是一个错误')
    }
    深度拷贝方法3:迭代递归法 for...in
    // 迭代递归法:深拷贝对象与数组
    function deepClone(obj) {
        if (!isObject(obj)) {
            throw new Error('obj 不是一个对象!')
        }
        //判断传进来的是对象还是数组
        let isArray = Array.isArray(obj)  
        let cloneObj = isArray ? [] : {}
        //通过for...in来拷贝
        for (let key in obj) {
            cloneObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
        }
    
        return cloneObj
    }
    let result = deepClone(test)
    
    console.log(result)
    
    for (let key in result) {
        if (isObject(result[key]))
            console.log(`${key}相同吗? `, result[key] === test[key])
    }
    //我们发现,arr 和 obj 都深拷贝成功了,它们的内存引用已经不同了,但 func、date、reg 和 err 并没有复制成功,因为它们有特殊的构造函数。
    深度拷贝方法4:迭代递归法 Reflect 
    // 代理法
    function deepClone(obj) {
        if (!isObject(obj)) {
            throw new Error('obj 不是一个对象!')
        }
    
        let isArray = Array.isArray(obj)
        let cloneObj = isArray ? [...obj] : { ...obj }
        Reflect.ownKeys(cloneObj).forEach(key => {
            cloneObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
        })
    
        return cloneObj
    }
    
    let result = deepClone(test)
    
    console.log(result)
    
    for (let key in result) {
        if (isObject(result[key]))
            console.log(`${key}相同吗? `, result[key] === test[key])
    }
    //结果其实和使用for...in相同。

    2 lodash 中的深拷贝  https://github.com/lodash/lodash/blob/master/.internal/baseClone.js

      let result = _.cloneDeep(test)

      我们发现,arr、obj、date、reg深拷贝成功了,但 func 和 err 内存引用仍然不变 //这跟 lodash 中的 cloneableTags 有关

    3lodash自定义拷贝对象

    function customizer(value) {
      if (_.isElement(value)) {
        return value.cloneNode(true);
      }
    }
    
    var el = _.cloneDeepWith(document.body, customizer);
     
    console.log(el === document.body);
    // => false
    console.log(el.nodeName);
    // => 'BODY'
    console.log(el.childNodes.length);
    // => 20

    4小姐姐,在了解一下

      4.1:对象成环,当我们使用上述方法(for...in 与Reflect都会出现栈溢出的错误,但是lodash却可以)

        注意:因为 lodash 使用的是栈把对象存储起来了,如果有环对象,就会从栈里检测到,从而直接返回结果,悬崖勒马。这种算法思想来源于 HTML5 规范定义的结构化克隆算法,它同时也解释了为什么 lodash 不对 Error 和 Function 类型进行拷贝。

      4.2:键不是字符串而是 Symbol

        使用for...in拷贝的时候就会拷贝失败,因为 Symbol 是一种特殊的数据类型,它最大的特点便是独一无二,所以它的深拷贝就是浅拷贝

    改造for...in
    function deepClone(obj) {
        if (!isObject(obj)) {
            throw new Error('obj 不是一个对象!')
        }
    
        let isArray = Array.isArray(obj)
        let cloneObj = isArray ? [] : {}
        let symKeys = Object.getOwnPropertySymbols(obj)
        // console.log(symKey)
        if (symKeys.length > 0) {
            symKeys.forEach(symKey => {
                cloneObj[symKey] =  isObject(obj[symKey]) ? deepClone(obj[symKey]) : obj[symKey]
            })
        }
        for (let key in obj) {
            cloneObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
        }
    
        return cloneObj
    }

        使用Reflect是可以的

      4.3 for...in会拷贝原型上面的属性,对于下面代码,只有for..in能够成功,而其他的方法都不能追踪到原型上面

    let childTest = Object.create(test)
    let result = deepClone(childTest)

        4.4 对象上的属性又分为可枚举属性和不可枚举属性,如何拷贝不可枚举的属性呢?

    //定义对象
    Object.defineProperties(test, {
        'obj': {
            writable: false,
            enumerable: false,
            configurable: false
        },
        'arr': {
            get() {
                console.log('调用了get')
                return [1,2,3]
            },
            set(val) {
                console.log('调用了set')
            }
        }
    })
    
    function deepClone(obj, hash = new WeakMap()) {
        if (!isObject(obj)) {
            return obj
        }
        // 查表,防止循环拷贝
        if (hash.has(obj)) return hash.get(obj)
    
        let isArray = Array.isArray(obj)
        // 初始化拷贝对象
        let cloneObj = isArray ? [] : {}
        // 哈希表设值
        hash.set(obj, cloneObj)
        // 获取源对象所有属性描述符
        let allDesc = Object.getOwnPropertyDescriptors(obj)
        // 获取源对象所有的 Symbol 类型键
        let symKeys = Object.getOwnPropertySymbols(obj)
        // 拷贝 Symbol 类型键对应的属性
        if (symKeys.length > 0) {
            symKeys.forEach(symKey => {
                cloneObj[symKey] = isObject(obj[symKey]) ? deepClone(obj[symKey], hash) : obj[symKey]
            })
        }
    
        // 拷贝不可枚举属性,因为 allDesc 的 value 是浅拷贝,所以要放在前面
        cloneObj = Object.create(
            Object.getPrototypeOf(cloneObj),
            allDesc
        )
        // 拷贝可枚举属性(包括原型链上的)
        for (let key in obj) {
            cloneObj[key] = isObject(obj[key]) ? deepClone(obj[key], hash) : obj[key];
        }
    
        return cloneObj
    }

        总结:微笑中透露着贫穷

      

     
    不忘初心,不负梦想
  • 相关阅读:
    URL统一资源定位符的组成
    B/S与C/S的比较
    springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑。为了区别不同的异常通常根据异常类型自定义异常类,这里我们创建一个自定义系统异常,如果controller、service、dao抛出此类异常说明是系统预期处理的异常信息。
    springmvc中Controller方法的返回值
    springmvc中@RequestMapping的使用
    构造完全图---最小生成树
    poj
    叶子的颜色---经典树上dp
    花神游历各国
    CodeForces
  • 原文地址:https://www.cnblogs.com/panrui1994/p/9378696.html
Copyright © 2011-2022 走看看