JavaScript数据复制艺术:深浅拷贝的奇妙探索

今天,我们踏上一段新的编程之旅,探索JavaScript中的"拷贝"。在现实生活中,我们常常会遇到复制的需求,比如复印文件、照片等,而在JavaScript中,拷贝则是数据复制的代名词,尤其是针对引用类型数据的操作。接下来让我们一起探索吧。✨

一、拷贝的现实映射与JavaScript中的含义

想象一下,你手头有一份重要文档,需要在不改变原文件的情况下,制作一份或多份副本。在JavaScript中,数据的"拷贝"也是如此,它是创建数据对象或数组副本的过程,以便在不影响原数据的情况下进行操作。

二、拷贝的双生子:浅拷贝与深拷贝

拷贝的世界分为两大阵营:浅拷贝与深拷贝,它们之间的区别在于对数据结构的复制程度。

  • 浅拷贝:如同复印文件时只复印了封面,内部的页码和内容还是与原文件共享。在JavaScript中,这意味着拷贝后的新对象与原对象在顶层结构上看起来相同,但内部如果有引用类型的属性(如对象、数组),新旧对象将共享这部分数据。一旦原对象的引用类型属性发生改变,拷贝对象也会随之变动。

  • 深拷贝:则是从封面到内容页的全面复制,每一页都是全新的。在JavaScript中,这意味着无论数据结构如何嵌套,深拷贝都能创建出一个与原对象完全独立的副本,任何对原对象的修改都不会影响到拷贝对象。

三、浅拷贝的日常应用

让我们通过几个方法,揭开浅拷贝的面纱:

1. Object.create(obj)

css 复制代码
const obj = { a: 1 };
const shallowCopy = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));

console.log(shallowCopy);  // { a: 1 }

这个方法创建了一个新对象,其原型指向obj,适合用于原型链的继承,但通常不作为浅拷贝的主要手段。

2. Object.assign({}, obj)

css 复制代码
const obj = { a: 1, b: { c: 2 } };
const newObj = Object.assign({}, obj);

console.log(newObj);  // { a: 1, b: { c: 2 } }

通过Object.assign,我们可以快速复制对象的可枚举属性,但嵌套对象仍然是引用共享。

3. [].concat(arr)[...arr]arr.slice(0)

数组的浅拷贝也很常见:

ini 复制代码
const array = [1, 2, { three: 3 }];
const concatCopy = [].concat(array);
const spreadCopy = [...array];
const sliceCopy = array.slice(0);

console.log(concatCopy,spreadCopy,sliceCopy);
// [ 1, 2, { three: 3 } ] [ 1, 2, { three: 3 } ] [ 1, 2, { three: 3 } ]

这些方法创建了数组的新实例,但数组内的对象还是共享的。

四、深拷贝的精妙之处

深拷贝是数据复制的高级形态,它确保了数据的完全独立性。

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

ini 复制代码
const obj = { a: 1, b: { c: 2 } };
const newObj = JSON.parse(JSON.stringify(obj));

console.log(newObj);  // { a: 1, b: { c: 2 } }

这个方法简单实用,但注意它无法处理函数、undefinedSymbol类型以及循环引用。

2. structuredClone(obj)

ini 复制代码
const obj = { a: 1, b: [97, 98, 99] };
const newObj = structuredClone(obj);

console.log(newObj);  // { a: 1, b: [ 97, 98, 99 ] }

现代浏览器提供了structuredClone,它几乎可以完美复制任何类型的值,包括循环引用。

五、手写拷贝方法:从零开始的创造

理解了浅拷贝和深拷贝的原理后,我们不妨自己动手实现这些过程。

1. 手写浅拷贝

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

Object.prototype.c = 3
let obj = {
    a: 1,
    b: {n: 2}
}

console.log(shallowCopy(obj));  // { a: 1, b: { n: 2 } }
}
  • 使用for...in循环遍历原对象的所有可枚举属性。
  • hasOwnProperty检查确保我们不复制原型链上的属性。
  • 对于每一个属性,直接将其值赋给新对象的同名属性,这一步对于基本类型是深拷贝,而对于引用类型则是浅拷贝,因为它们的值是引用地址。

2. 手写深拷贝

vbnet 复制代码
let obj = {
    a: 1,
    b: {n: 2}
}

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
}

console.log(deepCopy(obj));  // { a: 1, b: { n: 2 } }
  • 根据obj的构造函数创建新对象,保证新对象与原对象类型一致。
  • 使用for...in循环遍历并深拷贝每一个属性。
  • hasOwnProperty检查确保我们不复制原型链上的属性,instanceof Object判断是否属性值是对象,是的话使用递归调用deepCopy,不是就直接。
  • 最终返回构建好的新对象。

六、总结与思考

在JavaScript中,无论是浅拷贝还是深拷贝,都有其适用场景。浅拷贝简单快速,适用于不需要关心内部嵌套结构的情况;而深拷贝虽消耗更多资源,但提供了数据的完全独立性,对于复杂数据结构的复制至关重要。

通过本文的介绍,希望你能掌握JavaScript拷贝的精髓。深拷贝和浅拷贝没有好坏之分,正确地选择和应用拷贝方法,将使我们的代码更加牛叉。🎉

相关推荐
开心工作室_kaic1 小时前
springboot476基于vue篮球联盟管理系统(论文+源码)_kaic
前端·javascript·vue.js
川石教育1 小时前
Vue前端开发-缓存优化
前端·javascript·vue.js·缓存·前端框架·vue·数据缓存
搏博1 小时前
使用Vue创建前后端分离项目的过程(前端部分)
前端·javascript·vue.js
温轻舟2 小时前
前端开发 之 12个鼠标交互特效上【附完整源码】
开发语言·前端·javascript·css·html·交互·温轻舟
web135085886352 小时前
2024-05-18 前端模块化开发——ESModule模块化
开发语言·前端·javascript
LCG元3 小时前
javascript页面设计案例【使用HTML、CSS和JavaScript创建一个基本的互动网页】
javascript
技术程序猿华锋3 小时前
Gemini 2.0 Flash 体验版实测:日常视觉识别的最佳选择,关键在于其API Key现在是免费调用
开发语言·javascript·ecmascript·googlecloud·gemini
TttHhhYy4 小时前
uniapp+vue开发app,蓝牙连接,蓝牙接收文件保存到手机特定文件夹,从手机特定目录(可自定义),读取文件内容,这篇首先说如何读取,手机目录如何寻找
开发语言·前端·javascript·vue.js·uni-app
抓住鼹鼠不撒手4 小时前
xterm.js结合websocket实现web ssh
前端·javascript·websocket
QTX187304 小时前
ajax中get和post的区别,datatype返回的数据类型有哪些?web开发中数据提交的几种方式,有什么区别。
前端·javascript·ajax