深浅拷贝,温故知新

yizhihongxing

1、深拷贝

1.1、概念

对象的深拷贝是指其属性与其拷贝的源对象的属性不共享相同的引用(指向相同的底层值)的副本。

因此,当你更改源或副本时,可以确保不会导致其他对象也发生更改;也就是说,你不会无意中对源或副本造成意料之外的更改。

在深拷贝中,源和副本是完全独立的。深拷贝与其源对象不共享引用,所以对深拷贝所做的任何更改都不会影响源对象。

1.2、实现方式:

1.2.1、使用 JSON.stringify() 将该对象转换为 JSON 字符串,然后使用 JSON.parse() 将该字符串转换回(全新的)JavaScript 对象。

前提:JavaScript 对象可以被序列化

序列化异常报错

  • 存在循环引用时,会抛出异常 TypeError ("cyclic object value")(循环对象值)
  • 存在 BigInt 类型的值时,如:{ num: BigInt(1111111111) } 会抛出 TypeError ("BigInt value can't be serialized in JSON")(BigInt 值不能 JSON 序列化).

序列化需要注意的隐式转换

  • 非数组对象的属性不能保证以特定的顺序出现在序列化后的字符串中
  • undefined、任意的函数以及 symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null(出现在数组中时)。函数、undefined 被单独转换时,会返回 undefined,如 JSON.stringify(function(){}) or JSON.stringify(undefined).
  • 所有以 symbol 为属性键的属性都会被完全忽略掉,即便 replacer 参数中强制指定包含了它们。
  • Date 日期,会调用 toJSON() 将其转换为 string 字符串(同 Date.toISOString())
  • NaNInfinity 格式的数值及 null 都会被当做 null
  • Map/Set/WeakMap/WeakSet,仅会序列化可枚举的属性
  • HTML 元素对象, 会得到 {}

示例

  // 隐式转换

  JSON.stringify([new Number(1), new String("false"), new Boolean(false)]);
  // '[1,"false",false]'

  JSON.stringify({x: undefined, y: Object, z: Symbol("")});
  // '{}'

  JSON.stringify([undefined, Object, Symbol("")]);
  // '[null,null,null]'

  JSON.stringify({[Symbol("foo")]: "foo"});
  // '{}'

  JSON.stringify({[Symbol.for("foo")]: "foo"}, [Symbol.for("foo")]);
  // '{}'

  JSON.stringify(
      {[Symbol.for("foo")]: "foo"},
      function (k, v) {
          if (typeof k === "symbol"){
              return "a symbol";
          }
      }
  );

  // undefined

  // 不可枚举的属性默认会被忽略:
  JSON.stringify(
      Object.create(
          null,
          {
              x: { value: 'x', enumerable: false },
              y: { value: 'y', enumerable: true }
          }
      )
  );

  // "{"y":"y"}"

  JSON.stringify(document.body) // '{}'

  JSON.stringify(new Set([1, 2, 3])) // '{}'

  JSON.stringify(new Map([['num', 2]])) // '{}'
1.2.2、使用 window.structuredClone(),使用结构化克隆算法将给定的值进行深拷贝。

前提:可序列化的对象,且在浏览器环境和任何其他实现了 window 这样全局对象的 JavaScript 运行时的环境(structuredClone() 不是 JavaScript 语言本身的特性)

优点

  • 支持把原始值中的可转移对象转移到新对象,而不是把属性引用拷贝过去。
    • 可转移对象与原始对象分离并附加到新对象; 它们不可以在原始对象中访问被访问到。
  • 支持循环引用

补充:结构化克隆算法

  • 结构化克隆算法是用于复制复杂 js 对象的算法。
  • 它通过递归输入对象来构建克隆,同时保持先前访问过的引用的映射,以避免无限遍历循环。
  • Worker 的 postMessage()IndexedDB 存储对象时内部使用该算法。所以,也可间接的通过这2个方法实现深拷贝。

补充:可转移对象

  • 可转移的对象(Transferable object)是拥有属于自己的资源的对象,这些资源可以从一个上下文转移到另一个,确保资源一次仅在一个上下文可用。传输后,原始对象不再可用;它不再指向转移后的资源,并且任何读取或者写入该对象的尝试都将抛出异常。
  • 可转移对象通常用于共享资源,该资源一次仅能安全地暴露在一个 js 线程中。
  • 如:ArrayBuffer 是一个拥有内存块的可转移对象。当此类缓冲区(buffer)在线程之间传输时,相关联的内存资源将从原始的缓冲区分离出来,并且附加到新线程创建的缓冲区对象中。原始线程中的缓冲区对象不再可用,因为它不再拥有属于自己的内存资源了。

异常报错

  • Function 对象是不能被结构化克隆算法复制的,抛出 DATA_CLONE_ERR 异常,如:structuredClone(function fn() {})
  • Symbol 不能被结构化克隆算法复制的,抛出 DATA_CLONE_ERR 异常,如:structuredClone({s: Symbol(1)})
  • HTML 元素对象,抛出 DATA_CLONE_ERR 异常,如:structuredClone(document.body)

隐式转换

  • RegExp 对象的 lastIndex 字段不会被保留
  • 属性描述符,setters 以及 getters(以及其他类似元数据的功能)同样不会被复制。例如,如果一个对象用属性描述符标记为 read-only,它将会被复制为 read-write,因为这是默认的情况下
  • 原形链上的属性也不会被追踪以及复制

支持的js类型

支持的 Error类型

示例

// 循环引用
const original = { name: "MDN" };
original.itself = original;

// clone
const clone = structuredClone(original);

// 验证
console.log(clone !== original) // true
console.log(clone.name === "MDN") // true
console.log(clone.itself === clone) // true


const get = { get foo() { return 'bar' } }
console.log(get.foo) // 'bar'


class MyClass { 
  foo = 'bar' 
  myMethod() { /* ... */ }
}
const myClass = new MyClass()

const cloned = structuredClone(myClass)
// { foo: 'bar' }

cloned instanceof myClass // false
1.2.3、 使用js库

比如 lodash 中的 cloneDeep() 方法

该方法会递归拷贝 value。

clone 方法参考自 结构化克隆算法 以及支持 arraysarray buffersbooleansdate objectsmapsnumbersObject 对象, regexes, sets, strings, symbols, 以及 typed arraysarguments 对象的可枚举属性会拷贝为普通对象。 一些不可拷贝的对象,例如 error objectsfunctions, DOM nodes, 以及 WeakMaps 会返回空对象。

参考:MDN

2、浅拷贝

2.1、概念

对象的浅拷贝是其属性与拷贝源对象的属性共享相同引用(指向相同的底层值)的副本。

因此,当你更改源或副本时,也可能导致其他对象也发生更改——也就是说,你可能会无意中对源或副本造成意料之外的更改。

在浅拷贝中,对源或副本的更改可能也会导致其他对象的更改(因为两个对象共享相同的引用)。

2.2、实现方式

在 js 中,所有标准的内置对象进行操作:...展开语法、Array.prototype.concat()Array.prototype.slice()Array.from()Object.assign()Object.create(),创建的都是浅拷贝而不是深拷贝。

参考:MDN 浅拷贝

最后,感谢您阅读这篇博客!希望本文能够为您提供有价值的信息和启示。

如果您对本文的内容有任何疑问或建议,请随时在评论区留言,我会尽快回复您。

原文链接:https://www.cnblogs.com/EnSnail/p/17390161.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:深浅拷贝,温故知新 - Python技术站

(0)
上一篇 2023年5月11日
下一篇 2023年5月11日

相关文章

  • javascript针对DOM的应用分析(二)

    JavaScript针对DOM的应用分析 在使用JavaScript时,操作DOM是很重要的一部分。在本攻略中,我们将详细讨论JavaScript如何应用于DOM。 DOM基础知识 DOM(Document Object Model,文档对象模型)是指用于 HTML 和 XML 文档的编程接口。DOM 将整个页面抽象为一个树形结构,页面中的每个组成部分都是树…

    JavaScript 2023年6月10日
    00
  • JavaScript弹出对话框的三种方式

    当我们想向用户展示一些提示信息时,经常会使用JavaScript弹出对话框。JavaScript弹出对话框有三种方式,分别为alert()、confirm()和prompt()。 alert() 使用alert()方法弹出对话框可以显示警告信息,警告信息通常只需要用户确认即可。下面是alert()的语法: alert("警告信息"); 下…

    JavaScript 2023年5月27日
    00
  • 获取阴历(农历)和当前日期的js代码

    获取阴历(农历)和当前日期是网站中常见的功能之一。使用JavaScript实现这个功能需要借助第三方库或者手写算法。 一、使用第三方库 推荐使用lunar-calendar这个JavaScript库,该库提供了方便的API实现将阳历日期转换为阴历日期。在获取当前日期的情况下,可以直接使用JavaScript的Date对象获取。具体步骤如下: 下载lunar-…

    JavaScript 2023年5月27日
    00
  • JS浏览器BOM常见操作实例详解

    JS浏览器BOM常见操作实例详解 JS浏览器BOM(Browser Object Model)是指浏览器对象模型,它提供了与浏览器窗口进行交互的API。BOM包含了window、navigator、document等对象,这些对象是直接映射到浏览器窗口的,可以通过JS编程来操作浏览器窗口。本文将详细讲解JS浏览器BOM常见操作实例,包括获取浏览器窗口尺寸、打…

    JavaScript 2023年6月10日
    00
  • 详解JavaScript数组的常用方法

    详解JavaScript数组的常用方法 简介 在JavaScript中,数组是一种非常常见的数据类型。数组可以包含多种类型的数据,包括数字、字符串、对象等等。JavaScript提供了一系列的数组方法,可以方便地操作数组数据。在本篇文章中,我们将会介绍一些经常使用的JavaScript数组的方法。 push() push()方法可以向数组的末尾添加一个或多个…

    JavaScript 2023年5月27日
    00
  • JS 作用域与作用域链详解

    当我们使用 JavaScript 编写代码时,经常会遇到变量作用域的问题,这个时候就需要了解 JavaScript 的作用域与作用域链。了解 JavaScript 的作用域与作用域链,可以帮助我们更好地理解 JavaScript 代码运行的流程,从而提高编程效率和代码质量。 一、作用域 1.1 什么是作用域 作用域是指变量和函数的可访问范围。JavaScri…

    JavaScript 2023年6月10日
    00
  • 原生JavaScript实现拖动校验功能

    这里是原生JavaScript实现拖动校验功能的完整攻略。 1. 准备工作 在实现拖动校验功能之前,需要准备以下几项工作。 1.1 HTML结构 首先,需要在HTML中创建一个<ul>列表,每条列表项包含一个可拖动的元素,如下所示: <ul id="drag-items"> <li class="d…

    JavaScript 2023年6月10日
    00
  • 一次微信小程序内地图的使用实战记录

    下面我将详细讲解使用微信小程序内地图的操作步骤和注意事项,以及两条示例说明。 一、前期准备 1. 开通小程序云开发环境 首先需要开通小程序云开发环境,可以参考微信官方文档进行操作。 2. 获取高德地图API Key 在使用高德地图之前,需要在高德开放平台上注册账号,并获取API Key。具体流程可以参考高德地图官方文档。 3. 安装必要的npm包 使用微信小…

    JavaScript 2023年6月11日
    00
合作推广
合作推广
分享本页
返回顶部