前端面试--深拷贝与浅拷贝(附源码)

文章目录

    • 一行代码的深拷贝
    • 基础回顾:引用类型和值类型
      • 值类型
      • 引用类型
    • 深拷贝与浅拷贝的区别
      • 浅拷贝
      • 深拷贝
    • 常见业务场景
    • 深拷贝和浅拷贝的实现方式
      • 浅拷贝的实现
      • 深拷贝的实现

一行代码的深拷贝

如果不是面试需求,不想深究原理,只为了业务。那直接复制如下代码就可以实现简单的深拷贝

JSON.parse(JSON.stringify(copyObj))

stringify() 方法可以将一个JS对象序列化一个JSON字符串,parse()方法可以将JSON字符串反序列化为一个JS对象。通过这两个方法可以实现对象的深拷贝。

利用上述代码做深拷贝存在2个缺点:

  • 如果你的对象里有函数,函数无法被拷贝下来,原因是在序列化JS对象的过程中,所有的函数和原型成员会被有意的忽略。
  • 无法拷贝copyObj对象原型链上的属性和方法
    然而,大多数的业务中,需要深拷贝的对象是不需要函数和原型链相关内容的。

基础回顾:引用类型和值类型

理解JS里的引用类型和值类型的区别,知道Obj存储的只是引用。

值类型

var a = 100; 
var b = a;
a = 200;
console.log(a, b); // 200, 100

每个变量都会保存各自的值,不会相互影响。

引用类型

var a = {age:20};
b = a;
b.age = 21;
console.log(a.age);  //  21 

值类型和引用类型的区别:
从内存中来说,值类型是把一个值都存成一个变量类型的位置,如a是100,b赋值成a,这时b的位置又被赋值成了100。修改a位置的值不会影响到b的位置。
引用类型,a赋值给一个对象,a内存的对象通过指针指向这个对象, b赋值给a之后,a和b同时指向这一个对象。{age:20}这个对象只有一份,只不过a和b同时指向这个对象。
引用类型:对象,数组和函数( 引用类型都是可以无限赋值属性的 )

var a = { x: 1 }
var b = a
console.log(a.x, b.x)  // 1,  1
a.x = 2
console.log(a.x, b.x)  // 2,  2  ;{x:1}这个对象只有一份,只不过a和b同时指向这个对象。
a = { x: 3 }  // 直接改变了a指向的对象
console.log(a.x, b.x)  // 3,  2
var c = 3;
var d = c
console.log( c,  d) // 3,  3
c =4
console.log( c,  d) // 4,  3

深拷贝与浅拷贝的区别

只有对象里嵌套对象的情况下,才会根据需求讨论,我们要深拷贝还是浅拷贝。
比如:

var a = {
	x: 1,
	y: 2,
	z: 3
}

只有在有嵌套的对象时,深拷贝和浅拷贝才有区别

浅拷贝

拷贝后对象的引用,指向了相同的内存空间。
obj2是浅拷贝obj1生成的,虽然obj1和obj2是两个对象了,但obj1和obj2上的属性是===的。

 var obj2 = shallowCopy( obj1 , {})
 console.log( obj1 !== obj2 )// true 
 // 无论哪种拷贝,obj1和obj2一定都是2个不同的对象(内存空间不同)
 console.log( obj2.arr === obj1.arr )// true
 // 他们2个对象里arr的引用,指向【相同的】内存空间

深拷贝

obj2拷贝obj1所有的属性,而且obj2.arr和obj1.arr是指向不同的内存空间。
深拷贝后,除了拷贝了一样的属性,没有任何其他关联。

var obj2 = deepCopy( obj1 , {})
console.log( obj1 !== obj2 ) // true
// 无论哪种拷贝,obj1和obj2一定都是2个不同的对象(内存空间不同)
console.log( obj2.arr === obj1.arr ) // false
// 他们2个对象里arr的引用,指向【不同的】内存空间

常见业务场景

点击一列,编辑该列的信息。在弹出的编辑弹出框中,需要复制现有的列信息内容。一般这个值就是一个对象。如果使用了浅拷贝,在弹出框中编辑对象的属性,就会引起原有列信息的变动,因为指向了同一个对象。所以在类似业务场景中,必须使用深拷贝。

// 表格对象的数据结构
var tableArr = [
        {goods_name : 'aaa' , code : 'M216C239E0864' , num : '2'},
        {goods_name : 'bbb' , code : 'M216C240B0170' , num : '3'},
        {goods_name : 'ccc' , code : 'M216D241C04106' , num : '3'},
    ]
var adjustTableArr = []   // 编辑弹出框的数组
for (var key in tableArr) {    
    // 简单浅拷贝
    adjustTableArr[key] = tableArr[key]
}
// 结论:发现编辑弹出框数据adjustTableArr和原有tableArr的属性随动

深拷贝和浅拷贝的实现方式

浅拷贝的实现

浅拷贝比较简单,就是用for in 循环赋值

function shallowCopy(source, target = {}) {
        var key;
        for (key in source) {
            if (source.hasOwnProperty(key)) {    
                // 可选:原型链不拷贝,__proto__上面的属性
                target[key] = source[key];
            }
        }
        return target; 
}

深拷贝的实现

  • 深拷贝,就是遍历那个被拷贝的对象, 先判断对象里每一项的数据类型,而不是简单的赋值。
  • 如果不是对象类型,就直接赋值。如果是对象类型,就再次调用deepCopy,递归的去赋值,直到它是值类型再赋值。
function deepCopy (source) {
  // 判断source是数组还是对象,选定容器
  let target = Array.isArray(source) ? [] : {}
  for (let key in source) {
    if (typeof (source[key]) === 'object') {
      // 如果这一项是object类型,就递归调用deepCopy
      target[key] = Array.isArray(source[key]) ? [] : {}
      deepCopy(source[key], target[key])
    } else {
      // 如果不是object类型,就直接赋值拷贝
      target[key] = source[key]
    }
  }
  return target
}

你可能感兴趣的:(面试,前端面试)