JavaScript面试题~深浅拷贝

106 阅读3分钟

深浅拷贝

浅拷贝

指的是赋值拷贝 引用数据类型的内存地址,如果是简单数据类型拷贝的是值, 引用数据类型拷贝的是地址,也就是说单层对象没问题, 多层嵌套就会新数组改变,原数组也会跟着改变,影响最初的值.

特点 
    一个修改 另一个也改变
    本质上 是有一个引用数据类型
    多个变量中存储的是相同的内存地址

深拷贝

指的是赋值拷贝 引用数据类型的数据数值,深浅拷贝只针对引用数据类型, 对于简单数据类型直接拷贝他的值, 对于引用数据类型, 在堆内存中开辟一块内存用于存放复制的对象, 并把原有的对象类型数据拷贝过来, 这两个对象互相独立, 属于两个不同的内存地址, 修改其中一个,另一个不会发生改变

注意:

  1. 先数组, 后对象
  2. 实现深拷贝的方式
    // 引用数据类型 数组
    // 变量arr中存储的是 数组的内存地址
    const arr = [100,200,300,400,500];

    // 将数组arr中存储的内存地址 赋值给变量newArr1 存储 
    // 这里执行的是 浅拷贝
    const newArr1 = arr ; 

    // 创建一个新数组
    const newArr2 = [];

    // 深拷贝的执行结果 
    // 将 原始数组arr 中 每一个数据 赋值给 新数组newArr2 存储
    // 变量newArr2 中 存储的是 和 变量arr 不同的内存地址
    // 只是 两个不同的存储空间中 存储了相同的数据数值
    // 一个变量对应的存储空间中 存储的数据数值改变 
    // 另一个存储空间 不会受到影响
    newArr1.forEach(item=>{
        newArr2.push(item);
    })


    // 浅拷贝的执行结果
    // 也就是 变量arr 和 变量newArr1 中 存储的是 相同的一个数组的内存地址
    // 调用的是同一个内存空间中存储的数据数值 
    // 也就是 一个变量执行操作 另一个变量的调用也会受到影响
        arr[0] = '北京' ;

        console.log( arr );
        console.log( newArr1 );
        console.log( newArr2 );
       

实际项目中的深浅拷贝



const arr = [100,200,300,[400,500,600,[700,800,900]]];

const obj = { name:{n1:'张三' , n2:'张三三' , n3:'张33333'} , age:18 , sex:'男' , addr:['北京' , '上海' , '广州'] };

// // 如果只是直接循环遍历 一维是深拷贝 多维是浅拷贝
// const newArr = [] ;
// arr.forEach( item => newArr.push( item ) );

// arr[0] = '北京' ;
// arr[3][0] = '上海' ;

// console.log( arr );
// console.log( newArr );

// 实际项目中的深拷贝1 递归函数 完成深拷贝

// 有一个新数组 
const newArr = [] ;

// 将 原始数组arr 中的数据 深拷贝新数组newArr中
deepCopy( arr , newArr );

arr[3][0] = '上海' ;

console.log( arr );
console.log( newArr );


const newObj = {} ;
deepCopy( obj , newObj );

obj.addr[0] = 600 ;

console.log( obj );
console.log( newObj );


// 递归完成深拷贝的函数
function deepCopy( oldVal , newVal ){
    // oldVal 是 原始数据结构
    // newVal 是 新数据结构

    // for...in循环遍历原始数据结构 
    for( let key in oldVal ){
        // key 是 数组的索引下标 或者 对象的键名
        // oldVal[key] 是 数组的数据数值 或者 对象的键值

        if( Object.prototype.toString.call( oldVal[key] ) === '[object Array]' ){
            // 如果数据是 数组类型
            // 新数据结构 起始应该是 空数组
            newVal[key] = [] ;

            // 再次调用函数 执行 深拷贝
            deepCopy( oldVal[key] , newVal[key] );


        }else if( Object.prototype.toString.call( oldVal[key] ) === '[object Object]' ){
            // 如果数据是 对象类型 
            // 新数据结构 起始应该是 空对象
            newVal[key] = {} ;

            // 再次调用函数 执行 深拷贝
            deepCopy( oldVal[key] , newVal[key] );

        }else{
            // 如果数据不是 对象 也不是 数组 
            // 直接 赋值拷贝
            newVal[key] = oldVal[key] ;

        }

    }
}

// // 数组的数据类型判断结果 [object Array]
// console.log( Object.prototype.toString.call( [] ) );

// // 对象的数据类型判断结果 [object Object]
// console.log( Object.prototype.toString.call( {} ) );


// 实际项目中的深拷贝2  json字符串

// 将 原始数组/原始对象 转化为 json字符串 
// 再将 json字符串 还原成对应的数据结构 赋值给变量存储 
// 变量中存储的就是新的应用数据类型

 const a = JSON.parse( JSON.stringify( arr ) );

 arr[3][0] = '北京';

 console.log( a );
 console.log( arr );


 const b = JSON.parse( JSON.stringify( obj ) );

 b.addr[0] = 300 ;

 console.log( b );
 console.log( obj );