三分钟手搓深浅拷贝实现原理

前言

手搓深浅拷贝是面试官的常考题,在上一篇的学习中我们深度学习了拷贝,熟悉运用了深浅拷贝的方法。在本文中我们将利用拷贝的原理手搓一个函数来实现拷贝的功能。

浅拷贝的实现原理

如何实现浅拷贝?

首先我们要知道通俗来讲拷贝就是将一个对象中的属性全部复制到另一个集合里面。全部复制--那就需要遍历。在数组中,我们可以轻松的利用for()进行遍历,原理是for能使用计时器迭代每一个数组的索引位置(就是从0开始的下标) 。但是在对象中,并不存在下标值。所以我们需要用新的循环方法for...in来遍历对象。 我们需要先熟悉for...in的用法 对于对象obj来举例:

js 复制代码
let obj ={
   a:1,
   b:2,
   c:3
}

我们通过for(let key in obj) 可以访问obj 的所有可枚举属性。let key 会声明一个新的块级作用域的变量 key,用于在每次迭代中存储属性的属性名。我们同样可以通过obj[key]获取到属性的值。 通过:

js 复制代码
for (let key in obj) {
    console.log(key, obj[key]);
}

得到:

遍历对象是拷贝的第一步通过遍历对象得知对象中的所有属性后我们需要将这些属性放入到一个新的集合中,所以我们let newObj = {}这个集合就是拷贝后的对象。

现在我们进行浅拷贝的核心操作:将引用地址复制。如何实现?仔细想想,复制引用地址不就是将原来的属性值放到新对象中吗,正好用上我们刚学习方法:newObj[key] = obj[key]--这样就满足了"当原对象改变时,复制的对象也会改变"的浅拷贝规则,这段代码也是浅拷贝的根本原理。

还需要考虑到的是:在拷贝时绝大多数情况下希望拷贝的往往是显示属性,不希望复制对象隐式具有的属性,所以对象的隐式原型一般都不拷贝。但是for..in语句会遍历对象身上具有的显示属性和隐式属性,如何屏蔽掉对象的隐式属性呢?我们需要一个判断,运用obj.hasOwnProperty(key)方法可以解决这个问题。

obj.hasOwnProperty(key)方法可以判断对象身上的属性是隐式还是显性的--当对象obj中的属性key为显性时,返回true;当为隐性时,返回false。结合if判断,最终只保留显示属性进行拷贝。

完整代码如下:

js 复制代码
let obj = {
    name: 'Tom',
    like: {
        a: 'food'
    }
}
function shallow(obj) {
    let newObj = {}
   
    for (let key in obj) {
      
        if (obj.hasOwnProperty(key)) {
            newObj[key] = obj[key]
        }
    }
      return newObj
}

小结一下

  1. 借助for in 遍历原对象,将原对象的属性值增加到新对象中,
  2. 因为for in会遍历到对象的隐式属性,通常要使用 obj.hasOwnProperty(key)来判断要拷贝的属性是不是对象显示具有的

深拷贝的实现原理:

在上一文中我们已经说过,基本数据(原始数据)类型的拷贝就是值拷贝,他在效果上和深拷贝相同,手搓深浅拷贝,是涉及到对象和数组等复杂数据类型的拷贝。在掌握浅拷贝之后,在浅拷贝的基础上我们来考虑深拷贝的问题。

当遍历你需要复制的对象时,浅拷贝不会继续遍历对象中的对象的属性,只会复制引用地址;但是深拷贝需要将对象中的元素,对象中的对象中的元素...通通遍历,拿到每一个属性的值才能将他们确切的放在一个新集合里,因为复制的是每一个确切的值,所以拷贝过来的值是独立的值,原值的改变不会影响拷贝过来的值。那我们要怎么将每一个属性,每一个对象都遍历呢?对象里有对象,层层套娃怎么办?

看过我扁平化详解那篇文章的同学应该知道,运用递归的知识能很好的将对象中的嵌套一层层打开。所以我们只要先判断要复制的对象中是否有对象,如果有,就放到外面的大函数中递归解套。我们直接实操:用typeof 判断对象中的属性obj[key]是不是对象object,同时这个属性不能为空。

完整代码如下:

js 复制代码
const user = {
    name: {
        1: '掘',
        2: '金'
    },
    age: 18
}

function deep() {
    let newObj = {}
    
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            if (typeof (obj[key]) === 'object' && obj[key] !== null) {
            //判断是不是对象
             newObj[key] = deep(obj[key])
             //递归,每次都能创建出一个新对象,防止共用引用地址

            }else{
                //原始类型直接赋值
                newObj[key] = obj[key]
            }
        }
    }
    return newObj
}

小结:

  1. 借助for in 遍历原对象,将原对象的属性值增加到新对象中,
  2. 因为for in会遍历到对象的隐式属性,通常要使用
  3. 如果遍历到的属性值是原始数据类型,直接往对象中赋值,如果是引用类型,递归创建新的子对象
相关推荐
Yaml41 分钟前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
PleaSure乐事3 分钟前
【React.js】AntDesignPro左侧菜单栏栏目名称不显示的解决方案
前端·javascript·react.js·前端框架·webstorm·antdesignpro
哟哟耶耶4 分钟前
js-将JavaScript对象或值转换为JSON字符串 JSON.stringify(this.SelectDataListCourse)
前端·javascript·json
getaxiosluo4 分钟前
react jsx基本语法,脚手架,父子传参,refs等详解
前端·vue.js·react.js·前端框架·hook·jsx
理想不理想v8 分钟前
vue种ref跟reactive的区别?
前端·javascript·vue.js·webpack·前端框架·node.js·ecmascript
知孤云出岫8 分钟前
web 渗透学习指南——初学者防入狱篇
前端·网络安全·渗透·web
贩卖纯净水.14 分钟前
Chrome调试工具(查看CSS属性)
前端·chrome
测试19981 小时前
2024软件测试面试热点问题
自动化测试·软件测试·python·测试工具·面试·职场和发展·压力测试
栈老师不回家1 小时前
Vue 计算属性和监听器
前端·javascript·vue.js
前端啊龙1 小时前
用vue3封装丶高仿element-plus里面的日期联级选择器,日期选择器
前端·javascript·vue.js