JS手写浅拷贝与深拷贝

目录

1、引言

2、深拷贝与浅拷贝介绍

2.1、概念

2.2、实现方式

3、手写代码


1、引言

要了解浅拷贝与深拷贝,首先要知道 的概念

堆栈: 就是存放数据的地方(不管是定义的数字、字符串、对象还是数组、函数等等,都会在堆或栈中开辟内存空间存放该变量。 而保存在栈内存的必须是大小固定的数据引用类型的大小不固定,所以只能保存在堆内存中

基本数据类型(存放在栈中):number、string、boolean、null、undefined

复杂数据类型(存放在堆中):对象、数组、函数

扫描二维码关注公众号,回复: 14619659 查看本文章

如上图所示,内存空间中存在内存栈和内存堆。如果是基本类型,直接存储它们的值,而如果是引用类型,会在内存堆中开辟空间进行存储,而栈中变量值其实是堆的地址,指向堆中的这个数据

JS的变量类型分为 基本类型引用类型

而深拷贝与浅拷贝的区别,其实主要是针对于 引用类型的

对于基本类型的复制,只是对它们的值进行拷贝

2、深拷贝与浅拷贝介绍

2.1、概念

浅拷贝: 创建一个新对象,这个新对象有着原始对象属性值的一份拷贝。

  • 如果属性是基本类型,拷贝的就是基本类型的
  • 如果属性是引用类型,拷贝的就是内存地址
  • 拷贝时,开辟一块新的内存,将原始的属性的值(基本数据类型)或地址(引用数据类型)拷贝到新开辟的内存。
  • 浅拷贝只复制属性指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存,修改对象属性会影响原对象
     

深拷贝:将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,新旧对象不共享内存,且修改新对象不会影响原对象

 

 对于基本类型a和引用类型c,深拷贝为a1和c1,其中c1是在堆中额外开辟新空间,并完整的拷贝了c这个数组。 这时候修改c, 是不会影响c1的

2.2、实现方式

那在JS里面对于这两种拷贝各自有什么实现方式呢?

浅拷贝:

  • Object.assign
  • 数组的concat方法
  • 数组的slice方法
  • 对象扩展运算符

1、Object.assign(target,source) : 将多个源对象中的属性复制到一个目标对象中

let person_obj = {
    person: {
        name:'eric',
        age: 18
    }
}
let obj = Object.assign({},person_obj)
console.log(obj == person_obj)
obj.person.name = 'jerry'
console.log(person_obj)

 


2、 展开运算符

let man = {
    name: 'tutu',
    age: 100,
    address: {
        country: 'china',
        city: 'beijing'
    }
}
let man_copy = {...man}
man.address.city = 'shanghai'
man.name = 'yiyi'
console.log(man_copy);


3、数组concat方法

let arr = [1,2,3,{name:'lala'}]
let arr2 = arr.concat()
arr2[3].name = 'bibi'
console.log(arr);


4、数组slice方法 

let array = [1,2,{address:'beijing'}]
let array1 = array.slice()
array[2].address = 'shanghai'
console.log(array1);


深拷贝:

  • JSON.stringify()
  •  循环递归

1、JSON.parse(JSON.stringify())

最简单的深拷贝方法,就是把一个对象序列化为JSON的字符串,并将对象里面的内容转为字符串,最后JSON.parse()将其再生成一个对象

let arr = [1,2,{name:'eric'}]
let newArr = JSON.parse(JSON.stringify(arr))
arr[0] = 2
arr[2].name = 'tom'
console.log(arr);
console.log(newArr);

 但需要注意:

  • 拷贝的对象的值如果有函数,undefined,symbol 这几种类型,经过 JSON.stringify 序列化后字符串中这个键值对会消失。
  • 拷贝 Date 类型会变成字符串
  • 无法拷贝不可枚举的属性
  • 无法拷贝对象原型链
  • 拷贝 RegExp 引用类型会变成空对象
  • 对象中含有 NaN、infinity 以及 -infinity,JSON 序列化后的结果变成 null
  • 无法拷贝对象的循环应用,即对象成环(obj[key]=obj)

3、手写代码

浅拷贝

function shallowCopy(target) {
    if(typeof target === 'object' && target !== null){  // 引用类型
        const copy = Array.isArray(target) ? [] : {}  // 创建数组或对象
        for(const prop in target){
            // 判断当前对象是否有自身的属性 不包括继承
            if(target.hasOwnProperty(prop)){
                copy[prop] = target[prop]
            }
        }
        return copy
    }
    // 基础类型,直接返回
    return target
}

深拷贝

简单版深拷贝

function deepCopy(obj){
    let copyObj = {}
    for(const key in obj){
        //  null 的 typeof 也是 object
        if(typeof obj[key] === 'object' && obj[key] !== null) {
            copyObj[key] = deepCopy(obj[key])
        }else{
            copyObj[key] = obj[key]
        }
    }
    return copyObj
}

const obj = {
    name: 'eric',
    age: 18,
    address: {
        country: 'china',
        city: 'beijing'
    }
}

const obj1 = deepCopy(obj)
obj1.name = 'tom'
obj1.address.city = 'shanghai'
console.log(obj,obj1); 

 上面,我们利用递归方式实现了一个简单的深拷贝。但依旧存在一些问题:

考虑数组

考虑循环引用

考虑Date或RegExp

function deepCopy(obj, hash = new WeakMap){
    // null或undefined
    if(obj == null) return obj
    // Data、RegExp、Error
    if(obj instanceof Date) return new Date(obj)
    if(obj instanceof RegExp) return new RegExp(obj)
    if(obj instanceof Error) return new Error(obj.message)
    // 基本类型
    if(typeof obj !== 'object') return obj
    // 对象进行深拷贝,判断是否存在该对象
    if(hash.has(obj)) return hash.get(obj)
    // 原型上的方法,可以获取对象上所有属性级特性
    const desc = Object.getOwnPropertyDescriptor(obj)
    // 获取原型上的方法和对象的描述信息,创建新的对象
    const copyObj = Object.create(Object.getPrototypeOf(obj), desc)
    hash.set(obj, copyObj)

    // 循环递归遍历内容
    for(const key of Reflect.ownKeys(obj)){  // Reflect.ownKeys 返回所有属性,包括不可枚举属性和Symbol类型
        let item = obj[key]
        if (typeof item === 'object' && item !== null && typeof item !== 'function') {
            copyObj[key] = deepCopy(item)
        } else {
            copyObj[key] = item
        }
    }
    return copyObj
}

const obj1 = {
    func: function() {console.log(1)},
    obj: { name: 'h' , data: { fn: function() { console.log('data') }, child: 'child' }},
    arr: [1,2,3],
    und: undefined,
    ref: /^123$/,
    date: new Date(),
    NaN: NaN,
    infinity: Infinity,
    sym: Symbol(1)
}
console.log(deepCopy(obj1));         


 

猜你喜欢

转载自blog.csdn.net/m0_56698268/article/details/129809049