JavaScript 中的拷贝机制

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

拷贝的基本概念

在讨论拷贝之前,需要明确对象和基本类型的区别。JavaScript 中的基本类型(如number、string、boolean、undefined、null、symbol、bigint)是按值传递的,而引用类型(如object、array、function)是按引用传递的。

栗子🌰:

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

在上述例子中,基本类型 a 和 b 是独立的,而引用类型 obj1 和 obj2 共享同一个地址,因此对 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开发中非常重要的概念。浅拷贝适用于对象层级较浅的情况,而深拷贝则适用于需要完全复制对象的所有层级的情况。在实际开发中,应根据具体需求选择合适的拷贝方法,以确保代码的正确性和性能。

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

相关推荐
郑州光合科技余经理8 分钟前
海外版外卖加盟:总站与分站配送规则怎么分开管
java·开发语言·前端·后端·uni-app·php·ai编程
小呆呆6661 小时前
副业搞起来,小说,漫画,漫剧的成本优化思路
前端·后端·面试
凤城老人1 小时前
从 PyQt6 到 Electron:给 Edge TTS 做一个“多角色配音机“的踩坑手记
javascript·typescript·electron
Dovis(誓平步青云)1 小时前
浇水提醒刚弹出又消失,植物状态别只存一个百分比
开发语言·前端·javascript·pdf·ecmascript·电脑
lsc_blog2 小时前
远程和居家办公的经历,技术简历上怎么摆
面试·职场和发展·重构·pdf·求职招聘
码艺-Alimjan2 小时前
Vben Admin 新增维吾尔语 Vben-Modal的关键坑之一
前端·javascript·vue.js
可乐鸡翅yeah_2 小时前
hls.js 手动自定义 http 请求 loader,修改请求头实战
开发语言·前端·javascript·网络协议·http·ecmascript·m3u8在线
IT_陈寒2 小时前
Vite静态资源导入这个坑我帮你们踩过了
前端·人工智能·后端
广州华水科技3 小时前
大坝安全监测解决方案:单北斗GNSS形变监测系统应用与维护
前端
时间的拾荒人3 小时前
Qt 事件机制全解析:从事件处理到事件过滤器
开发语言·qt·面试