首页 > 其他分享 >一道比亚迪前端面试题:深拷贝和浅拷贝有什么区别?

一道比亚迪前端面试题:深拷贝和浅拷贝有什么区别?

时间:2024-12-20 11:27:12浏览次数:6  
标签:面试题 const log console newObj obj 拷贝 比亚迪

大家好,我是小寒。

最近有朋友参加了比亚迪的前端面试,又被问到了一道经典的前端面试题:深拷贝和浅拷贝有什么区别? 本期一起来盘一下这道题。

一、深拷贝和浅拷贝

浅拷贝的特点:

  • 只拷贝第一层
  • 对于基本数据类型,拷贝值
  • 对于引用数据类型,拷贝引用

深拷贝的特点:

  • 多层递归拷贝
  • 对于基本数据类型,拷贝值
  • 对于引用数据类型,创建一个新的引用对象,并循环拷贝到新对象中

举个例子:

const obj = {
    a: 1,
    b: [1, 2, 3],
    c: {
        d: 4,
        e: 5
    }
}

const newObj = deepClone(obj);
console.log(newObj);
/**
    打印结果:
    {
        a: 1,
        b: [1, 2, 3],
        c: {
            d: 4,
            e: 5
        }
    }
 */

对于上面的例子来说,newObj的打印结果都是相同的,不同的是,对于浅拷贝来说:

  • obj.b === newObj.b
  • obj.c === newObj.c

而对于深拷贝来说,它们前后都是不相等的。

二、浅拷贝的实现方式

2.1 展开运算符

可以用ECMAScript 2018 规范新增特性,也就是ES6提供的语法三个点(…)来实现深拷贝,具体用法如下:

const newObj = { ...obj };

2.2 Object.assign

Object.assignObject类自带的一个静态方法,可以将一个或多个对象中的可枚举(此属性的enumerable为true)自有属性(对象自身的,从原型上继承的不算)合并到目标对象中。

const newObj = Object.assign({}, obj1, obj2, ...);

这里要注意一下,展开运算符和Object.assign虽然可以实现浅拷贝,但仍有细微的区别,比如在遇到settergetter时,两者的表现不一样。来举个例子:

首先是展开运算符:

const obj1 = {
    get a() {
        console.log('getter')
        return 1;
    },
    set a(val) {
        console.log('setter')
    }
}
const obj2 = {
    a: 2,
}

const newObj = { ...obj1, ...obj2 };
console.log(newObj);
console.log(newObj.a)

console.log('------------------------');

const newObj1 = { ...obj2, ...obj1 };
console.log(newObj1);
console.log(newObj1.a)


/**
 * 打印结果:
  getter
  { a: 2 }
  2
  ------------------------
  getter
  { a: 1 }
  1
 */

从打印结果可以看出,展开运算符在拷贝时有如下特点:

  1. 合并时不会执行setter;
  2. 合并后同名getter和属性会同时存在,但取值时会按照合并的先后顺序,会先取后合并的值。

然后是Object.assign

const obj1 = {
    get a() {
        console.log('getter')
        return 1;
    },
    set a(val) {
        console.log('setter')
    }
}
const obj2 = {
    a: 2,
}

const newObj = Object.assign(obj1, obj2);
console.log(newObj);
console.log(newObj.a)

console.log('------------------------');

const newObj1 = Object.assign(obj2, obj1);
console.log(newObj1);
console.log(newObj1.a);

/**
 * 打印结果:
  setter
  { a: [Getter/Setter] }
  getter
  1
  ------------------------
  getter
  { a: 1 }
  1
 */

从打印结果可以看出,``Object.assign`在拷贝时有如下特点:

  1. 同名属性和同名gettersetter合并时,会执行setter,而同名gettersetter同名属性合并时却不会执行setter。
  2. 无论同名属性和同名gettersetter的合并先后顺序如何,最终访问只会访问到getter里面的值,只是从控制台里看的效果不一样而已。

2.3 for…in + Object.prototype.hasOwnProperty

直接用for..in循环,配合hasOwnProperty判断是否是自身的属性来进行拷贝。

function shallowClone(obj) {
    const newObj = {};
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
        newObj[key] = obj[key];
        }
    }
    return newObj;
}

const obj = {
    a: 1,
    b: [1, 2, 3],
    c: {
        d: 4,
        e: 5
    }
};
const newObj = shallowClone(obj);
console.log(newObj)

或者用Object.keys先拿到自身的属性的数组,然后forEach循环拷贝也可,方式有很多种,大家可以自行扩展。

三、深拷贝的实现方式

3.1 JSON.parse + JSON.stringify

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

这种方式实现深拷贝十分简单,我在开发中也经常使用,不过这种方式实现深拷贝有以下缺点:

  1. 无法处理循环引用,遇到会报错。
  2. 不支持functionundefined,拷贝后会丢失。
  3. 无法复制某些对象类型, 遇到比如Date、RegExp、Map、Set、Error、Symbol 等 JavaScript 内置对象及其属性时,会被转换为基本的字符串或数组形式,导致前后的数据类型不一样,不符合预期。

尽管如此,它仍然在实际开发中广泛使用,比如拷贝表单数据进行前后对比等场景。

3.2 使用第三方库

比如使用loddash

const _  = require('lodash');
const newObj = _.cloneDeep(obj);
console.log(newObj);

3.3 手动实现深拷贝

const isObj = (target) => typeof target === 'object' && target !== null
function deepClone(obj, hash = new WeakMap()) {
    if (!isObj(obj)) return obj;
    if (hash.has(obj)) return has.get(obj);
    const target = new obj.constructor();
    hash.set(obj, target);
    Object.keys(obj).forEach((key) => {
        target[key] = deepClone(obj[key], hash);
    })
    return target;
}

const newObj = deepClone(obj);
console.log(newObj);

这里用WeakMap处理循环引用的问题,通过new obj.constructor直接构造对象、数组等对象的实例,然后forEach复制属性,简单实现一个深拷贝。

四、小结

本文主要分享了一道前端高频面试题,也就是深拷贝和浅拷贝的区别,希望小伙伴们读完后对浅拷贝和深拷贝有更深入的理解。

这里也分享下比亚迪的全部前端面试题:

  1. 自我介绍;
  2. 实习中的项目业务介绍;
  3. 你现在用RN开发吗?那有没有用RN做过一些手机端的适配;
  4. react和vue你觉得哪个好用?为什么?
  5. vue的生命周期;
  6. 双向绑定与响应式原理;
  7. Vue的单向数据流;
  8. Vue的组件之间的通信;
  9. 你刚说到pinia通信,那你知道pinia的原理吗?和Vuex有什么区别?
  10. 怎么使用pinia?
  11. Vue-router的所有钩子函数介绍一下;
  12. vue的单页面和多页面的区别?
  13. 防抖和节流在实习项目中用过吗?
  14. vue的computed和watch的区别?
  15. 用过Vue的脚手架吗?
  16. vite的原理知道吗?
  17. react生命周期;
  18. js中要做异步操作该怎么办?
  19. 要清除定时器该怎么操作?
  20. 深拷贝和浅拷贝有什么区别?

大家觉得这个面试题难度如何呢?

标签:面试题,const,log,console,newObj,obj,拷贝,比亚迪
From: https://blog.csdn.net/m0_67275869/article/details/144520768

相关文章

  • 短期面试突击攻略大全!2025最全Java面试题目合集
     这两年的面试难度确实要比往年高处很多。很多小伙伴投递了上千份简历,只有几家公司约面试。排除个人简历的因素,这在往年都是不太常见的。大厂缩招,于是很多往年能进大厂的人只能去卷中小厂,搞得层层内卷。 比如往年能有一万个人能进大厂,今年大厂只招聘一千个,那另外九千个在往......
  • Springboot面试题+个人理解
    应该有相当一部分程序员是通过Springboot接触WEB开发的,但实际上他已经是一个进化到比较后期的框架了。WEB的大部分的功能都不是由Springboot实现的,它只做了一些简化开发的工作。对于倒着学框架的感受其实不怎么好,大部分时间都得保持一个“学了再说,会用就行”的态度,一旦你想......
  • 2024互联网1000多道Java常见面试题(附详细答案整理)
    相信进大厂是每个程序员最想做的事情吧?但是由于今年的大环境不好,好多厂裁员现象也层出不穷,尤其是腾讯、阿里、字节员工也不断爆出被裁的消息。而导致很多准备跳槽的小伙伴以为最近行情不好都不敢轻易跳槽,都在沿岸观望,想着等着行情好点再跳。事实并非如此。大厂裁员的原因很多......
  • 英飞源嵌入式面试题及参考答案
    如何在给定的连续内存空间中高效地实现一个队列和一个栈?栈是一种后进先出(LIFO)的数据结构,要在连续内存空间中实现栈,可以使用一个数组来存储栈元素。定义一个指针来指向栈顶元素,初始时栈为空,指针指向一个特殊值(比如-1或者数组的起始位置之前)。当进行入栈操作时,先将指针向上......
  • 硬件工程师面试题 11-20
        把常见的硬件面试题进行总结,方便及时巩固复习。其中包括网络上的资源、大佬们的大厂面试题,其中可能会题目类似,加强印象即可。11、示波器需要关注哪些参数?1,示波器带宽        带宽是示波器最重要的指标之一。        模拟示波器的带宽是一个......
  • 腾讯技术岗位笔试&面试题(三)
    说在前面本篇文章是腾讯技术面试题目汇总第三篇。后续将持续推出互联网大厂,如阿里,腾讯,百度,美团,头条等技术面试题目,以及答案和分析。欢迎大家点赞关注转发。最近有点忙,所以更新的比较忙一些1.野指针是什么?如何检测内存泄漏?野指针:指向内存被释放的内存或者没有访问权限......
  • 腾讯技术岗位笔试&面试题(五)
    说在前面本篇文章是腾讯技术面试题目汇总第五篇。后续将持续推出互联网大厂,如阿里,腾讯,百度,美团,头条等技术面试题目,以及答案和分析。欢迎大家点赞关注转发。1.define、const、typedef、inline使用方法?一、const与#define的区别:const定义的常量是变量带类型,而#define定......
  • 腾讯技术岗位笔试&面试题(四)
    说在前面本篇文章是腾讯技术面试题目汇总第四篇。后续将持续推出互联网大厂,如阿里,腾讯,百度,美团,头条等技术面试题目,以及答案和分析。欢迎大家点赞关注转发。原文链接:https://mp.weixin.qq.com/s/9EpKvEJECIh6rrd_Kqdfkw1.__stdcall和__cdecl的区别?__stdcall__stdcal......
  • 2024年Qt面试题汇总
    2024年Qt面试题汇总1.请说说Qt的D指针(d_ptr)与Q指针(q_ptr)1.1D指针(d_ptr)1.2Q指针(q_ptr)2.常用的Qt布局有几种,如何自适应缩放?3.Qt信号和槽的本质是什么4.描述Qt中的文件流(QTextStream)和数据流(QDataStream)的区别5.Qt程序是事件驱动的,事件到处都可以遇到......
  • 8 SQL 面试题
    SQL基础面试题两个表:TableX有三个字段Code、Name、Age、其中Code为主键:TableY有三字字段Code、Course(课程)、Score(成绩),其中Code+Courese为主键:------------TableX-------------Code Name Age97001 张三 2297002 赵四2197003 张飞 2097004 李五 22-------------TableY------......