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

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

相关推荐
adminIvan2 分钟前
Element plus使用menu时候如何在折叠时候隐藏掉组件自带的小箭头
前端·javascript·vue.js
会发光的猪。21 分钟前
【 ElementUI 组件Steps 步骤条使用新手详细教程】
前端·javascript·vue.js·elementui·前端框架
我家媳妇儿萌哒哒22 分钟前
el-table合并单元格之后,再进行隔行换色的且覆盖表格行鼠标移入的背景色的实现
前端·javascript·elementui
baiduguoyun37 分钟前
react的import 导入语句中的特殊符号
前端·react.js
前端青山39 分钟前
webpack指南
开发语言·前端·javascript·webpack·前端框架
NiNg_1_2341 小时前
ECharts实现数据可视化入门详解
前端·信息可视化·echarts
励志前端小黑哥2 小时前
有了Miniconda,再也不用担心nodejs、python、go的版本问题了
前端·python
喵叔哟2 小时前
重构代码之取消临时字段
java·前端·重构
还是大剑师兰特2 小时前
D3的竞品有哪些,D3的优势,D3和echarts的对比
前端·javascript·echarts
王解2 小时前
【深度解析】CSS工程化全攻略(1)
前端·css