今天,CVTE面试官问了深拷贝和浅拷贝的问题

我的回答是:浅拷贝是拷贝了对象的引用,当原对象发生变化的时候,拷贝对象也跟着变化;深拷贝是另外申请了一块内存,内容和原对象一样,更改原对象,拷贝对象不会发生变化;

但是面试官给我说:浅拷贝是拷贝一层,深层次的对象级别的就拷贝引用;深拷贝是拷贝多层,每一级别的数据都会拷贝出来;

回来查了一下资料,并没有发现面试官说的这种关于深拷贝浅拷贝的说法,看了几篇文章,终于理解了

其实总结来看,浅拷贝的时候如果数据是基本数据类型,那么就如同直接赋值那种,会拷贝其本身,如果除了基本数据类型之外还有一层对象,那么对于浅拷贝而言就只能拷贝其引用,对象的改变会反应到拷贝对象上;但是深拷贝就会拷贝多层,即使是嵌套了对象,也会都拷贝出来。

实现浅拷贝的第一种方法

/*** Created by 811 on 2018/7/27.*/
function simpleClone(initalObj) {var obj = {};for ( var i in initalObj) {obj[i] = initalObj[i];}return obj;
}var obj = {a: "hello",b:{a: "world",b: 21},c:["Bob", "Tom", "Jenny"],d:function() {alert("hello world");}
};
var cloneObj = simpleClone(obj);console.log(cloneObj.a);
console.log(cloneObj.b);
console.log(cloneObj.c);
console.log(cloneObj.d);//更改原对象中的a,b,c,d,看看拷贝过来的对象是否变化
cloneObj.a = "changed";
cloneObj.b.a = "changed";
cloneObj.b.b = 25;
cloneObj.c = [1, 2, 3];
cloneObj.d = function() { alert("changed"); };
console.log(obj.a);    //hello
console.log(obj.b);    //{a:"changed",b:25},事实上就是只有对象是拷贝的引用类型
console.log(obj.c);    //['Bob','Tom','Jenny']
console.log(obj.d);    //...alert("hello world")


事实证明面试官说的是对的,浅拷贝就是拷贝了一层,除了对象是拷贝的引用类型,其他都是直接将值传递,有自己的内存空间的。

实现浅拷贝的第二种方法

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

Object.assign(target, …sources)
参数:

target:目标对象。
sources:任意多个源对象。
返回值:目标对象会被返回。

var obj1 = {a: "hello",b: {a: "hello",b: 21}
};var cloneObj1= Object.assign({}, obj1);
cloneObj1.a = "changed";
cloneObj1.b.a = "changed";
console.log(obj1.a);  //hello
console.log(obj.b.a); // "changed"

另外,(这里其实没太看懂),为什么上面的改了,这里却没有改。。。。说是可以拷贝深一层,如果对象只有一层的话,可以使用这个函数作为深拷贝的方法

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

深拷贝的实现方式

1、手动复制

把一个对象的属性复制给另一个对象的属性

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

但这样很麻烦,要一个一个自己复制;而且这样的本质也不能算是 Deep Copy,因为对象里面也可能回事对象,如像下面这个状况:

var obj1 = { body: { a: 10 } };
var obj2 = { body: obj1.body };
obj2.body.a = 20;
console.log(obj1);
// { body: { a: 20 } } <-- 被改到了
console.log(obj2);
// { body: { a: 20 } }
console.log(obj1 === obj2);
// false
console.log(obj1.body === obj2.body);
// true

虽然obj1跟obj2是不同对象,但他们会共享同一个obj1.body,所以修改obj2.body.a时也会修改到旧的。

2、对象只有一层的话可以使用上面的:Object.assign()函数

Object.assign({}, obj1)的意思是先建立一个空对象{},接着把obj1中所有的属性复制过去,所以obj2会长得跟obj1一样,这时候再修改obj2.b也不会影响obj1。

因为Object.assign跟我们手动复制的效果相同,所以一样只能处理深度只有一层的对象,没办法做到真正的 Deep Copy。不过如果要复制的对象只有一层的话可以考虑使用它。

3、转成 JSON 再转回来

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

var obj1 = { body: { a: 10 } };
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.body.a = 20;
console.log(obj1);
// { body: { a: 10 } } <-- 沒被改到
console.log(obj2);
// { body: { a: 20 } }
console.log(obj1 === obj2);
// false
console.log(obj1.body === obj2.body);
// false

这样做是真正的Deep Copy,这种方法简单易用。

但是这种方法也有不少坏处,譬如它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。

这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,即那些能够被 json 直接表示的数据结构。RegExp对象是无法通过这种方式深拷贝。

也就是说,只有可以转成JSON格式的对象才可以这样用,像function没办法转成JSON。

var obj1 = { fun: function(){ console.log(123) } };
var obj2 = JSON.parse(JSON.stringify(obj1));
console.log(typeof obj1.fun);
// 'function'
console.log(typeof obj2.fun);
// 'undefined' <-- 没复制

要复制的function会直接消失,所以这个方法只能用在单纯只有数据的对象。

4、递归拷贝

function deepClone(initalObj, finalObj) {    var obj = finalObj || {};    for (var i in initalObj) {        if (typeof initalObj[i] === 'object') {obj[i] = (initalObj[i].constructor === Array) ? [] : {};            arguments.callee(initalObj[i], obj[i]);} else {obj[i] = initalObj[i];}}    return obj;
}
var str = {};
var obj = { a: {a: "hello", b: 21} };
deepClone(obj, str);
console.log(str.a);

上述代码确实可以实现深拷贝。但是当遇到两个互相引用的对象,会出现死循环的情况。

为了避免相互引用的对象导致死循环的情况,则应该在遍历的时候判断是否相互引用对象,如果是则退出循环。

改进版代码如下:

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) ? [] : {};            arguments.callee(prop, obj[i]);} else {obj[i] = prop;}}    return obj;
}
var str = {};
var obj = { a: {a: "hello", b: 21} };
deepClone(obj, str);
console.log(str.a);

5、使用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;
}

原文链接:https://blog.csdn.net/weixin_37719279/article/details/81240658

JS的深拷贝和浅拷贝相关推荐

  1. js中深拷贝和浅拷贝问题

    最近在学习c++,以前从来没接触过也没想到过深拷贝和浅拷贝.c++里有深拷贝和浅拷贝,那么其它语言中应该也有. 本文参考: https://juejin.im/post/59ac1c4ef265da2 ...

  2. js实现深拷贝与浅拷贝

    思路 a.基本类型–名值存储在栈内存中,例如let a=1; 当你b=a复制时,栈内存会新开辟一个内存: 所以当你此时修改a=2,对b并不会造成影响,因为此时的b已自食其力,翅膀硬了,不受a的影响了. ...

  3. 一篇文章彻底说清JS的深拷贝/浅拷贝

    一篇文章彻底说清JS的深拷贝and浅拷贝 这篇文章的受众 第一类,业务需要,急需知道如何深拷贝JS对象的开发者. 第二类,希望扎实JS基础,将来好去面试官前秀操作的好学者. 写给第一类读者 你只需要一 ...

  4. js 浅拷贝直接赋值_js 深拷贝 vs 浅拷贝

    本文主要讲一下 js 的基本数据类型以及一些堆和栈的知识和什么是深拷贝.什么是浅拷贝.深拷贝与浅拷贝的区别,以及怎么进行深拷贝和怎么进行浅拷贝. 本文思维导图如下:本文思维导图 堆和栈的区别 其实深拷 ...

  5. js深拷贝和浅拷贝对数组的影响_浅拷贝与深拷贝(JavaScript)

    一.预备知识 ECMAScript变量包含两种不同数据类型的值:基本数据类型和引用数据类型. 基本数据类型:名值存储在栈内存中: 引用数据类型:名存在栈内存中,值存在于堆内存中,但是栈内存会提供一个引 ...

  6. js深拷贝和浅拷贝对数组的影响_javaScript中的深拷贝和浅拷贝

    今天来整理一下js中的深浅拷贝 深浅拷贝 简单来说,假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着A一起变了,说明这是浅拷贝,如果B没有变,那就是深拷贝. 看例题 //案例1var a1 ...

  7. 详解JS深拷贝与浅拷贝

    深拷贝与浅拷贝 深拷贝和浅拷贝都只针对引用数据类型,浅拷贝会对对象逐个成员依次拷贝,但只复制内存地址,而不复制对象本身,新旧对象成员还是共享同一内存:深拷贝会另外创建一个一模一样的对象,新对象跟原对象 ...

  8. js中的深拷贝和浅拷贝区别

    js中的深拷贝和浅拷贝与值传递和引用传递有着些许的关联,都是根据堆栈中数据的储存来传递数据. 下面主要说一下我对深拷贝和浅拷贝的理解: 简单举个例子来说明:当我们声明一个a变量并且赋值,并且让b等于a ...

  9. js深拷贝和浅拷贝的区别

    js深拷贝和浅拷贝的区别 如何来区分深拷贝和浅拷贝,其实简单,例如: 就是我声明一个obj对象,如何让var a直接等于obj,然后有在obj新增个fun,此时的a也会随着新增个fun,相同a新增数据 ...

最新文章

  1. 《计算机科学导论》一1.1 图灵模型
  2. python快速编程入门课本中的名片管理器_python实现名片管理器的示例代码
  3. ubuntu指令模式修改IP等信息
  4. dsge模型难做吗_百度Seo优化好做吗,现在做是不是越来越难做?
  5. 架构,改善程序复用性的设计~第二讲 什么应该提取出来,什么应该保留
  6. Android学习路线图
  7. python 论文降重_我用Python写了一个论文降重工具-Go语言中文社区
  8. kaggle 电商数据分析
  9. 绩效考核管理方案文档
  10. 简单的交换机下设备连接,路由器互通
  11. 怎么轻松卸载mysql_轻松将mysql卸载干净
  12. Android省市县三级联动 真实项目抽出 调用只需3行代码
  13. 万能的pdf转换成jpg转换器软件
  14. EduCoder-Web程序设计基础-html5—结构元素-第3关:figure元素和figcaption元素的应用
  15. 飞思卡尔芯片解密 MC9S08GB60 芯片特点
  16. 计算机器点游戏,24游戏计算器
  17. Android 在xml中更改纯色图片颜色
  18. STC89C52RC - 1 - 简述以及最小操作系统
  19. html5 localstorage 生命周期,cookie、localStorage和sessionStorage 三者之间的区别以及存储、获取、删除等使用方式...
  20. Centos8安装vsftpd

热门文章

  1. spring boot的热加载(hotswap)
  2. 局域网无法上网解决处理方法
  3. 《树莓派Python编程入门与实战(第2版)》——3.9 小结
  4. Struts框架核心技术小小班
  5. SpringCloud Eureka Client和Server侧配置及Eureka高可用配置
  6. 实现盒子动画和键盘特效
  7. 企业项目开发--分布式缓存memcached(3)
  8. 你应该知道的计算机网络知识
  9. OpenGL硬件加速指南
  10. Android EdgeEffect 使用 和 源码解析