RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1386561
Accepted
Боков Глеб
Боков Глеб
Asked:2022-08-01 18:18:15 +0000 UTC2022-08-01 18:18:15 +0000 UTC 2022-08-01 18:18:15 +0000 UTC

在 JavaScript 中创建任意对象的离线副本是真的吗?

  • 772

关于 JavaScript 中的深度复制对象有很多讨论,并且在 StackOverflow 上提供了许多英文版的解决方案。我什至有我自己的。

但在我再次承担这项任务之前,我想问一个问题——原则上是否可以自主克隆任意对象?许多解决方案(包括我的最新版本)没有考虑到:

  • 具有“函数”类型值的属性
  • Getter 和 Setter
  • 不可枚举的属性
  • Maps 和 Sets(更不用说 WeakMap 和 WeakSet)

我知道这条信息不仅被专业人士阅读,而且还被那些不了解问题深度的初学者阅读,因此可能很想回答这样的问题:“哈!是的,它是初级的!const clone = { ...original }-就是这样! !!”。但如果就这么简单,我在 Web 开发的第七年就不会问这个问题了,所以让我们分解并清除流行的误解。

但首先,让我们澄清定义。 深拷贝一个对象意味着更改克隆对象的任何属性,包括其所有子对象的属性,都不会以任何方式影响原始实例,而克隆对象与原始实例完全相同,包括属性函数,非- 可枚举的属性,等等。

错误决策的分析与排除

序列化后反序列化

在这个方法中,我们首先用 序列化对象,JSON.stringify(original)然后用 反序列化它JSON.parse。在一行中,它将是这样的:

JSON.parse(JSON.stringify(original))

让我们尝试克隆以下原始对象:

const original = {
    alpha: "FOO",
  bravo: () => { return "BAR" }
}

const clone = JSON.parse(JSON.stringify(original));

但是如果我们将它打印到控制台,那么bravo属性将会丢失。此外,这种方法对我们来说不再感兴趣。

Object.assign 和传播运算符

查看语法

const clone = { ...original }

或者

const clone = Object.assign({}, original )

不做深度克隆——事实上,这是用一个容器替换另一个容器,内容引用与原始对象相同的内存区域。

const original = {
    alpha: "FOO",
  bravo: {
    charlie: "BAR"
  }
}

const clone = { ...original };
clone.bravo.charlie = "HOGE";

console.log(original.bravo.charlie); // "HOGE"!!! Какого ... ?!

对于对象的特殊情况 - 数组也是如此:

const clone = [ ...original ];

好吧,正如 Dan Abramov 在他的 Redux 中所建议的那样,分别复制每个子对象是 21 世纪第三个十年的某种狂野——当我被提议做时,我可以谈论什么样的纳米技术、机器人技术和火星地球化这么日常的工作?这就是这种日常废话的样子:

function updateVeryNestedField(state, action) {
  return {
    ...state,
    first: {
      ...state.first,
      second: {
        ...state.first.second,
        [action.someId]: {
          ...state.first.second[action.someId],
          fourth: action.someValue
        }
      }
    }
  }
}

深度复制任意对象的问题

复制功能

首先,尚不清楚函数的深度克隆实际上意味着什么。从技术上讲,这可能意味着声明与现有函数相同的函数,但在不同的内存区域中。但也许我错过了一些东西。

我的职业生涯是从 ES6 语法开始的,所以我没有发现程序员使用函数作为类来敲钉子的时候。如果不考虑这些技巧,那么

function original() {

}

const clone = original;

虽然不会是深拷贝,但它会导致任何问题吗?

我在英语的 StackOverflow 上找到了这个解决方案:

const newFunc = oldFunc.bind({}); 

如果这样的解决方案不会引起任何副作用,那么我会注意到它。

一种或另一种方式,我们在这里谈论对象的深度克隆,特别是属性-函数(方法)。在那里,在方法内部,也可以有this,并且在深度复制期间,必须将链接指向this克隆。

复制 getter 和 setter

当对 getter 和 setter 进行迭代时Object.entries(original)(如我上面的解决方案),值将是这些 getter 和 setter 返回的值(并且由于 setter 本质上不返回任何内容,这将是undefined):

const original = {
  alpha: "FOO",
  get charle() {
    return this.alpha + "__" + "BAR";
  },
  set delta(value) {
    this.alpha = value;
  }
}

for (const [key, value] of Object.entries(original)) {
  console.log(key);
  console.log(value);
}

此处属性的值charle将是字符串FOO__BAR。

alpha
FOO
charle
FOO__BAR
delta
undefined

它不会像这样工作(会有相同的结果):

for (const key of Object.keys(original)) {
  // ...
}

不可枚举的属性

说实话,根据我的所有经验,我几乎从不需要迭代不可枚举的属性。但原则上,我们可以了解不可枚举属性 * 的存在(例如,通过Object.getOwnPropertyNames(object1)),甚至更多地转向它们,因此更进一步 - 上述所有问题。

地图和集合

Map 无法转换为 JSON,这可能会导致问题(我不确定我之前建议的解决方案是否可以通过改进来最终确定;可能需要从根本上改变方法)。

也许这些集合需要通过创建一个相同类型的新容器来单独处理。但毕竟,随着时间的推移,新的容器会被添加到 ES6 中,然后你将不得不对实现进行更改

最后一个问题

问题是:上述所有与派生对象的深度克隆有关的问题,以及我没有提及的问题,原则上是可以克服的吗?在创建深拷贝功能时可以考虑到这一点吗?或者,仅自主复制仅包含可序列化为 JSON 的数据类型(数字、字符串、布尔值、null、数组、对象)的对象是否现实?

javascript
  • 1 1 个回答
  • 10 Views

1 个回答

  • Voted
  1. Best Answer
    Qwertiy
    2022-08-01T18:28:43Z2022-08-01T18:28:43Z

    通常不可能创建一个完全独立的对象。

    从新的 - 无法以任何方式读取或传输的私有属性:

    class Smth {
      get #x() { return Math.random() }
      get x() { return this.#x }
    }
    
    var obj = new Smth()
    
    console.log(obj.x)
    console.log(obj.x)
    console.log(obj.x)

    从旧 - 所有关闭:

    var obj = (function () {
      var x = 0
      
      return {
        doSmth() {
          return ++x
        }
      }
    })()
    
    console.log(obj.doSmth())
    console.log(obj.doSmth())
    console.log(obj.doSmth())

    并且不复制原型、函数绑定 getter 和其他一切都无济于事。

    • 1

相关问题

  • 第二个 Instagram 按钮的 CSS 属性

  • 由于模糊,内容不可见

  • 弹出队列。消息显示不正确

  • 是否可以在 for 循环中插入提示?

  • 如何将 JSON 请求中的信息输出到数据表 Vuetify vue.js?

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5