技术

前端面试-深拷贝和浅拷贝

微信扫一扫,分享到朋友圈

前端面试-深拷贝和浅拷贝
收藏 0 0

面试题目:如何实现对一个数组或对象的浅拷贝和深拷贝?

WTF,复制还分两种,第一次遇到这种问题的时候很是无语呢,先来看看一般的答案的理解。

浅拷贝是只拷贝一层,深层次的对象级别就只拷贝引用。 深拷贝是拷贝多层,每一级别的数据都拷贝出来。也就是说,基本数据类型其实不存在深浅拷贝的问题,只有对象和数组才存在深浅拷贝的问题。

主要解决的是什么问题呢?你去买房子,看中一套不错要了,然后中介给你打印了一份合同,你签字付钱。过一段时间去看,哎呀我去,怎么装修了?另外一个人也拿着同样有合同、付款凭证。我以为是我买的房子,结果中介一房两卖,别人也能搞。这怎么行?

JS数据类型

js分为基本数据类型和复杂数据类型。

基本数据类型包括:String、Number、Boolean、Null、Undefined

复杂(引用)类型包括:Object、Array、Function

在开发过程中,经常使用 typeof 来检测数据类型。默认var声明的时候,如果不进行赋值,类型就是undefined。布尔值是boolean只有 true,false两种值。

声明的时候用的null,这时候代表空对象,使用typeof检测的时候,显示是Object。

JS内存管理

JS代码运行的时候,数据都要写入内存进行调用的,而不同的数据类型在内存中存放的方式是不一样的。

基本数据类型是存储在栈数据空间中,复杂数据类型是存储在堆数据空间中的,而对数据空间不能直接访问,需要栈这边进行位置指引。

一个不是很恰当的比喻就是内存相当于仓库。

仓库里面分了两个区域,一边是都是小格子,另一边都是大货柜。简单数据类型比如你的一本书,你的一份账单什么的就直接放在小格子里面就好了。

另外你有一屋子书和一屋子的账单,小格子放不下。你就租了一个小格子和一个仓库。小格子里面放着仓库的钥匙和仓库的位置,仓库里面放东西。

实际的内存读取方式也类似。要找自己的小格子,你就要从上到下挨着找。想要找自己货柜里面的东西,还是需要先去小格子里面找到存放钥匙和位置的格子,找到以后直接去找货柜。

下图是

浅拷贝和深拷贝

再没有了解到深拷贝和浅拷贝知识的时候,一般拷贝就是从新赋值。声明个数据直接用另外一个对象赋值。这个就算是浅拷贝。

当遇到复杂对象的时候,复制的只是对象的指针,并没有重新开辟大的空间进行复制。这时候造成的影响就是对两个指针进行数据操作的时候,操作的是同一个数据内容,相互之间是受影响的。

而深拷贝就是需要连指针到内容都进行复制,两个指针指向两个空间的内容。各自操作已经不受影响。

浅拷贝的实现方式

浅拷贝的复制就是直接复制赋值就可以了。

方法一:

function simpleClone(initalObj) {    
  var obj = {};    
  for ( var i in initalObj) {
    obj[i] = initalObj[i];
  }    
  return obj;
}

var obj = {
  b:{
      a: "world",
      b: 21
    },
  c:["Bob", "Tom", "Jenny"],
  d:function() {
      alert("hello world");
    }
}
var cloneObj = simpleClone(obj); 
console.log(cloneObj.b); 
console.log(cloneObj.c);
console.log(cloneObj.d);

cloneObj.b.a = "changed";
cloneObj.c = [1, 2, 3];
cloneObj.d = function() { alert("changed"); };
console.log(obj.b);
console.log(obj.c);
console.log(obj.d);

自行运行查看下变化及原因。

方法二: Object.assign是ES6的新函数。Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。

Object.assign(target, ...sources)
var obj = { a: {a: "hello", b: 21} };
var initalObj = Object.assign({}, obj);

initalObj.a.a = "changed";
console.log(obj.a.a); // "changed"

需要注意的是:

Object.assign()可以处理一层的深度拷贝,如下:

var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = Object.assign({}, obj1);
obj2.b = 100;
console.log(obj1);
// { a: 10, b: 20, c: 30 } <-- 沒被改到
console.log(obj2);
// { a: 10, b: 100, c: 30 }

深拷贝的实现方式

如果要复制的对象只有一层,对象里面的元素全是基本元素的话,前面的浅拷贝案例其实就完成了深拷贝的功能。我们重点说一下多层对象。

1.通过JSON转换 用JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象。

但是这种方法也有不少坏处,譬如它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。并且只能处理常见的Number, String, Boolean, Array, 扁平对象等这些能被JSON表示的数据结构。RegExp对象是无法通过这种方式深拷贝。

2.递归拷贝

function deepClone(initalObj, finalObj) {    
  var obj = finalObj || {};    
  for (var i in initalObj) {        
    var prop = initalObj[i];
    if(prop === obj) {            
      continue;
    }
    if (typeof prop === 'object') {
      obj[i] = (prop.constructor === Array) ? [] : {};            
      arguments.callee(prop, obj[i]);
    } else {
      obj[i] = prop;
    }
  }    
  return obj;
}

递归拷贝就是将对象逐层解开进行剖析,逐层新建对象,逐层复制,知道最深处的所有简单数据都复制上。

但是要注意要注意对象引用对象的情况,会掉入死循环。

3.使用Object.create()方法 直接使用var newObj = Object.create(oldObj),可以达到深拷贝的效果。

function deepClone(initalObj, finalObj) {    
  var obj = finalObj || {};    
  for (var i in initalObj) {        
    var prop = initalObj[i];        // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
    if(prop === obj) {            
      continue;
    }        
    if (typeof prop === 'object') {
      obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
    } else {
      obj[i] = prop;
    }
  }    
  return obj;
}

小案例

之前在进行公司VUE项目开发的过程中,由于需要将富文本编辑器抽离成为一个单独的组件,然后将内容的对象传入进去。如果按照传统的vue组件开发的流程,肯定是要接收传入、赋值给本组件、本组件编辑器修改、修改完毕的内容再进行emit外传,然后组件外部接受,进一步处理。

但是由于vue组件之间浅拷贝的特性,其实传入的对象修改之后,外部组件直接取值拿到的就是最新的值。

也是因为这个发现才对深拷贝和浅拷贝有了更加深入的了解。

文末

其实深拷贝和浅拷贝算是js开发过程中比较重要的部分,配合更加输入了解下JS的内存管理机制,对于进一步深入了解堆栈、闭包、内存回收等等高级问题的理解会有大大的帮助。

展开阅读全文
半拉子前端的自留地,发际线逐渐后移的抠脚大叔!

前端面试-JS事件流

上一篇

你不懂JS:入门与进阶(序\前言)

下一篇

你也可能喜欢

发表评论

您的电子邮件地址不会被公开。 必填项已用 * 标注

提示:点击验证后方可评论!

插入图片

体验小程序

标签地图

分类

EA PLAYER &

历史记录 [ 注意:部分数据仅限于当前浏览器 ]清空

      00:00/00:00

      微信扫一扫

      微信扫一扫