JavaScript 中的拷贝机制

在 JavaScript 中,理解浅拷贝和深拷贝是编写高效代码的基础。尤其在处理引用类型时,正确的拷贝方法能够避免意想不到的副作用。本文将详细讨论浅拷贝和深拷贝的概念、常用方法以及它们的实现原理,并通过具体的代码示例帮助你更好地掌握这些知识。

拷贝的基本概念

在讨论拷贝之前,需要明确对象和基本类型的区别。JavaScript 中的基本类型(如numberstringbooleanundefinednullsymbolbigint)是按值传递的,而引用类型(如objectarrayfunction)是按引用传递的。

栗子🌰:

javascript 复制代码
let a = 1;
let b = a;
a = 2;
console.log(b); // 1

let obj1 = { age: 18 };
let obj2 = obj1; // 引用赋值
obj1.age = 20;
console.log(obj2.age); // 20

在上述例子中,基本类型 ab 是独立的,而引用类型 obj1obj2 共享同一个地址,因此对 obj1 的修改会影响到 obj2。所以拷贝通常我们只讨论在引用类型上的拷贝。

浅拷贝

浅拷贝是创建一个新对象,这个新对象有着原对象属性值的精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是引用类型,拷贝的就是内存地址,因此新旧对象的引用类型属性仍然指向同一个地址,即拷贝后的对象受到原对象的影响。

常用方法:

  1. Object.create(obj1)
  2. Object.assign({}, obj1)
  3. [].concat(arr1)
  4. [...arr1]
  5. arr1.slice(0)
  6. arr1.toReversed().reverse()

以上方法都会返回一个新对象,只要把他们赋值到你想拷贝到的新对象上,就能模拟出浅拷贝的效果

浅拷贝的原理:

浅拷贝的实现可以通过遍历对象的属性,然后赋值给新对象来实现。但注意还要借助hasOwnProperty规避原对象隐式具有的属性,因为拷贝我们一般是不拷贝原对象上隐式属性的。

对于object.hasOwnProperty(propertyName) 如果对象自身拥有该属性,则返回 true。 如果对象自身没有该属性(即使在原型链上存在),则返回 false

手搓浅拷贝

知道了这些,我们就可以自己手搓一个方法来实现浅拷贝。

javascript 复制代码
function shallowCopy(obj) {
    let obj1 = {};
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            obj1[key] = obj[key];
        }
    }
    return obj1;
}

let obj = { a: 1, b: { n: 2 } };
console.log(shallowCopy(obj)); // { a: 1, b: { n: 2 } }

有人可能会问为什么不用obj.key来取对象上的属性,因为在这里key是一个变量,如果用obj.key的话,key会被当成一个字符串,这样取到的就是对象上属性名为'key'的对应的值。所以这里要用obj[key]

深拷贝

深拷贝是指完全拷贝一个对象及其所有嵌套对象,拷贝后的新对象与原对象完全独立,互不影响。

常用方法:

  1. JSON.parse(JSON.stringify(obj))
  2. structuredClone(obj)

JSON.parse(JSON.stringify(obj)) :

javascript 复制代码
let obj = {
    a: 1,
    b: { n: 2 },
    c: 'cc',
    d: true,
    e: undefined,
    f: null,
    g: function(){},
    h: Symbol(1),
    // i: 123n // BigInt 无法处理
};

let newObj = JSON.parse(JSON.stringify(obj));
console.log(newObj); // { a: 1, b: { n: 2 }, c: 'cc', d: true, f: null }

这种方法简单易用,但有一些限制:

  1. 无法处理 BigInt 类型。
  2. 无法拷贝 undefined、函数和 Symbol
  3. 无法处理循环引用。

structuredClone() :

javascript 复制代码
let obj = { a: 1, b: { n: 2 } };
const newObj = structuredClone(obj);
console.log(newObj); // { a: 1, b: { n: 2 } }

structuredClone 是一个新方法,能够处理大多数复杂的对象结构,但仍无法拷贝 Symbol 和函数,且有些浏览器可能不兼容。

深拷贝的原理:

深拷贝需要递归地拷贝对象的每一层属性,确保所有嵌套对象都能正确地深拷贝,即如果对象属性的值也是一个对象,则创建一个新对象,只能进行原始值的传递,而不是引用传递,不和原对象共用同一个地址。

手搓深拷贝:

javascript 复制代码
function deepCopy(obj){
    let newObj = {}
    for(let key in obj){
        if(obj.hasOwnProperty(key)){
            if(obj[key] instanceof Object){
                newObj[key] = deepCopy(obj[key])    
            }else{
                newObj[key] = obj[key]
            }
        }
    }
    return newObj
}

let obj = { a: 1, b: { n: 2 } };
let obj2 = deepCopy(obj);
obj.b.n = 1;
console.log(obj2.b.n); // 2

总结

浅拷贝和深拷贝是JavaScript开发中非常重要的概念。浅拷贝适用于对象层级较浅的情况,而深拷贝则适用于需要完全复制对象的所有层级的情况。在实际开发中,应根据具体需求选择合适的拷贝方法,以确保代码的正确性和性能。

通过本文的讲解,希望你能够深入理解浅拷贝和深拷贝的原理,并在实际开发中灵活运用这些知识。

相关推荐
m0_74825614几秒前
前端 MYTED单篇TED词汇学习功能优化
前端·学习
小马哥编程1 小时前
Function.prototype和Object.prototype 的区别
javascript
小白学前端6661 小时前
React Router 深入指南:从入门到进阶
前端·react.js·react
web130933203981 小时前
前端下载后端文件流,文件可以下载,但是打不开,显示“文件已损坏”的问题分析与解决方案
前端
王小王和他的小伙伴1 小时前
解决 vue3 中 echarts图表在el-dialog中显示问题
javascript·vue.js·echarts
学前端的小朱2 小时前
处理字体图标、js、html及其他资源
开发语言·javascript·webpack·html·打包工具
outstanding木槿2 小时前
react+antd的Table组件编辑单元格
前端·javascript·react.js·前端框架
小k_不小2 小时前
C++面试八股文:指针与引用的区别
c++·面试
好名字08212 小时前
前端取Content-Disposition中的filename字段与解码(vue)
前端·javascript·vue.js·前端框架
摇光932 小时前
js高阶-async与事件循环
开发语言·javascript·事件循环·宏任务·微任务