赋值、浅拷贝、深拷贝

105 阅读3分钟

js数据类型

基本数据类型

  • boolean
  • string
  • number
  • undefined
  • null
  • Symbol
  • bigInt

复杂数据类型

  • Object
  • Array
  • Function
  • Set
  • Map
  • RegExp
  • Date
  • 等等.....

数据存储方式

基本数据类型:存储在栈中

复杂数据类型:存储在堆中,会在栈中开辟一个空间,存储的是堆的地址

image.png

赋值

基本类型

        let a=1
        let b = a
        a=2
        console.log(a) // 2
        console.log(b) // 1

复杂类型

       let xf={
            name:'xf',
            age:17
        }
        let person = xf 
        console.log(xf===person) // true
        xf.name='jelly'
        console.log(xf)      // {name: "jelly", age: 17}
        console.log(person)  // {name: "jelly", age: 17}

基本类型赋值,是值的拷贝,复杂类型赋值,是赋值的地址,指向同一个内存地址

image.png

浅拷贝

浅拷贝:

  1. 生成新对象,会开辟新的地址空间来存储新对象
  2. 属性为基本类型,拷贝值(数据值),属性为复杂类型,拷贝值(地址值),修改拷贝后的数据会影响到源数据(同一个对象)

实现浅拷贝的方式

  1. 扩展运算符
let idols = [
            {
                name:'gil',
                age:17,
            },
            {
                name:'sa',
                age:17,
            }
        ]
let likes = [...idols]
console.log(likes) // [{name: "gil", age: 17}, {name: "sa", age: 17}]
likes[0].age=18    // 影响到了源数据idols
console.log(idols) // [{name: "gil", age: 18}, {name: "sa", age: 17} 
  1. slice
    let idols = [
        {
            name:'gil',
            age:18,
        },
        {
            name:'sa',
            age:17
        }
    ]
    let copyIdols = idols.slice()
    console.log(idols===copyIdols)  // false
    copyIdols[0].name='joy'
    console.log(copyIdols)          // [{name: "joy", age: 18}, {name: "sa", age: 17}]
    console.log(idols)              // [{name: "joy", age: 18}, {name: "sa", age: 17}]

  1. Object.assign()
        let xf={
            name:'xf',
            age:17,
            hobby:['sleep','eat']
        }
        let person = Object.assign({},xf) 
        console.log(person)        // {name: "xf", age: 17, hobby: ["sleep", "eat"]}
        person.hobby[0] = 'coding' // 影响到了源数据xf
        console.log(xf.hobby)     // ["coding", "eat"]
  1. 手写实现
      const shadowClone = function(obj){
            if(typeof obj!='object'|| obj === null) return obj
            let result = Array.isArray(obj)?[]:{}
            for(i in obj){
                result[i] = obj[i]
            }
            return result
        }

测试对象:

        let xf={
            name:'xf',
            age:17,
            hobby:['sleep','eat']
        }
        
        let copyXf = shadowClone(xf)
        console.log(copyXf)         // {name: "xf", age: 17, hobby: ["sleep", "eat"]}
        console.log(copyXf === xf) // false
        copyXf.hobby[0] = 'codding'
        console.log(xf)             // {name: "xf", age: 17, hobby: ["codding", "eat"]}

测试对象数组:

        let idols = [
            {
                name:'gil',
                age:17,
            },
            {
                name:'sa',
                age:17,
            }
        ]
        
        let copyIdols = shadowClone(idols)
        console.log(copyIdols)       // [{name: "gil", age: 17}, {name: "sa", age: 17}] 
        console.log(copyIdols === idols) // false
        copyIdols[0].age= 18
        console.log(idols)           // [{name: "gil", age: 18}, {name: "sa", age: 17}]

测试普通数组:

        let idols = [ 'gil','sa']
        
        let copyIdols = shadowClone(idols)
        console.log(copyIdols)         //  ["gil", "sa"]
        console.log(copyIdols===idols) // false
        copyIdols[0] = 'joy'           
        console.log(idols)             //  ["gil", "sa"]

深拷贝

  1. JSON.Parse(JSON.stringify())
         let xf={
            name:'xf',
            age:17,
            hobby:['sleep','eat'],
            sex:undefined,
            getName:function(){},
            [Symbol()]:123
        }
        let copyObj = JSON.parse(JSON.stringify(xf))
        copyObj.hobby[0] = 'codding'
        console.log(xf)   // 
        console.log(copyObj)

image.png

从上面截图可以看到通过JSON.parse(JSON.stringify())实现的是深拷贝,但是他有一些缺陷,循环引用报错、忽略对象中的Symbol、function、undefined等等,关于JSON.parse和JSON.stringify的具体操作,可以查看我的另一篇文章 juejin.cn/post/723971…

  1. 手写实现
        const deepClone = function(obj,cache = new WeakMap()){
            // 基本数据类型-直接返回
            if(typeof obj !='object' || obj === null) return obj
            // // 检查缓存中是否已存在副本
            if(cache.has(obj)) return cache.get(obj)
            let result
            if(Array.isArray(obj)){
                result = []
                // 将原对象与新副本关联起来
                cache.set(obj,result)
                // 递归深拷贝数组元素
                for(let i = 0;i<obj.length;i++){
                    result.push(deepClone(obj[i],cache))
                }
            // 处理Set类型
            }else if(obj instanceof Set){
               result = new Set()
               cache.set(obj,result)
               obj.forEach(value=>{
                result.add(deepClone(value,cache))
               })
            // 处理Map类型
            }else if(obj instanceof Map){
                result = new Map()
                cache.set(obj,result)
                obj.forEach((value,key)=>{
                    result.set(deepClone(key,cache),deepClone(value,cache))
                })
            }else{
                result = {}
                // 将原对象与新副本关联起来
                cache.set(obj,result)
                // 递归深拷贝对象属性
                Reflect.ownKeys(obj).forEach(value=>{
                    result[value] = deepClone(obj[value],cache)
                })
            }
            return result
        }

测试用例:

   let obj={
            name:'xf',
            age:17,
            isGirl:true,
            money:undefined,
            job:null,
            friends:['z','x','c'],
            family:{
                father:'xxx',
                mother:'ccc',
            },
            x: new Set('1'),
            y: new Map([['food','sugar']]),
            z:Symbol(),
            [Symbol()]:'hahaha',
            a: 1,
            b: {
                c: 2,
                d: 3
            }
        }
        // 循环引用
        obj.a = obj.b
        obj.b.c = obj.a

        let copyObj = deepClone(obj)
        console.log(copyObj)
        copyObj.friends[0] = 'p'
        copyObj.family.father = '999'
        copyObj.x.add('3')
        copyObj.y.set('drink','water')
        console.log(copyObj)
        console.log(obj)

copyObj

image.png

obj

image.png

以上的deepClone()处理了Date,RegExp,Array,Object,Set,Map数据类型,也处理了循环引用问题,应该是考虑到了大部分的情况,如果有遗漏的情况,以后发现了再补充