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

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

相关推荐
家里有只小肥猫5 分钟前
uniApp小程序保存canvas图片
前端·小程序·uni-app
前端大全7 分钟前
Chrome 推出全新的 DOM API,彻底革新 DOM 操作!
前端·chrome
前端小臻35 分钟前
关于css中bfc的理解
前端·css·bfc
Ama_tor41 分钟前
网页制作05-html,css,javascript初认识のhtml表格的创建
javascript·css·html
白嫖不白嫖1 小时前
网页版的俄罗斯方块
前端·javascript·css
HappyAcmen1 小时前
关于Flutter前端面试题及其答案解析
前端·flutter
饼干饿死了1 小时前
实现动态翻转时钟效果的 HTML、CSS 和 JavaScript,附源码
javascript·css·html
顾比魁1 小时前
pikachu之CSRF防御:给你的请求加上“网络身份证”
前端·网络·网络安全·csrf
林的快手1 小时前
CSS文本属性
前端·javascript·css·chrome·node.js·css3·html5
一个 00 后的码农1 小时前
25会计研究生复试面试问题汇总 会计专业知识问题很全! 会计复试全流程攻略 会计考研复试真题汇总
经验分享·考研·面试·面试问题·25考研·考研复试·会计复试