{"id":15893946,"url":"https://github.com/chinanf-boy/clean-set-explain","last_synced_at":"2026-01-11T17:55:01.753Z","repository":{"id":90548135,"uuid":"138454599","full_name":"chinanf-boy/clean-set-explain","owner":"chinanf-boy","description":"explain: clean-set Quickly update a value 对象快速赋值","archived":false,"fork":false,"pushed_at":"2018-06-24T05:54:46.000Z","size":16,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-08T08:47:18.024Z","etag":null,"topics":["explain","ref"],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chinanf-boy.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-06-24T04:56:22.000Z","updated_at":"2018-06-24T05:54:47.000Z","dependencies_parsed_at":"2023-07-18T11:34:29.218Z","dependency_job_id":null,"html_url":"https://github.com/chinanf-boy/clean-set-explain","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chinanf-boy%2Fclean-set-explain","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chinanf-boy%2Fclean-set-explain/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chinanf-boy%2Fclean-set-explain/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chinanf-boy%2Fclean-set-explain/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chinanf-boy","download_url":"https://codeload.github.com/chinanf-boy/clean-set-explain/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246866100,"owners_count":20846496,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["explain","ref"],"created_at":"2024-10-06T08:14:07.344Z","updated_at":"2026-01-11T17:55:01.697Z","avatar_url":"https://github.com/chinanf-boy.png","language":null,"funding_links":["https://patreon.com/yobrave"],"categories":[],"sub_categories":[],"readme":"# clean-set [![explain](http://llever.com/explain.svg)](https://github.com/chinanf-boy/Source-Explain)\n\n「 Object 扩展运算符和Object.assign的深度赋值替代方法 」\n\nExplanation\n\n\u003e \"version\": \"1.1.0\"\n\n[github source](https://github.com/fwilkerson/clean-set)\n\n[中文](./readme.md) | ~~[english](./readme.en.md)~~\n\n## 使用\n\n\u003cdetails\u003e\n\n\u003csummary\u003e 更多信息 \u003c/summary\u003e\n\n``` js\nlet current = {\n  a: { b: [], c: true },\n  d: [],\n  e: {\n    f: { g: 'hello' },\n    h: { i: 0 },\n  },\n};\n\nlet next = cleanSet(current, 'e.h.i', 1);\n\n/**\n * 或者，您可以为最终参数提供一个函数\n  *接收该节点的当前值。\n *\n * let next = cleanSet(current, 'e.h.i', i =\u003e i + 1);\n */\n\n// The value is assigned\nconsole.log(next.e.h.i !== current.e.h.i); // true\n\n// Each parent node touched is a new reference\nconsole.log(next.e.h !== current.e.h); // true\nconsole.log(next.e !== current.e); // true\nconsole.log(next !== current); // true\n\n// Untouched references remain the same\nconsole.log(next.e.f === current.e.f); // true\nconsole.log(next.a === current.a); // true\nconsole.log(next.a.b === current.a.b); // true\nconsole.log(next.d === current.d); // true\n```\n\n\u003c/details\u003e\n\n---\n\n穷\n\n\u003ca href=\"https://patreon.com/yobrave\"\u003e\n\u003cimg src=\"https://c5.patreon.com/external/logo/become_a_patron_button@2x.png\" height=\"30\"\u003e\n\u003c/a\u003e\n\n\n---\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n\n- [package.json](#packagejson)\n- [index.js](#indexjs)\n  - [疑问copy](#%E7%96%91%E9%97%AEcopy)\n    - [两次 copy ?](#%E4%B8%A4%E6%AC%A1-copy-)\n    - [copy的不同](#copy%E7%9A%84%E4%B8%8D%E5%90%8C)\n- [⚠️ 警告](#-%E8%AD%A6%E5%91%8A)\n  - [隐形地址传递](#%E9%9A%90%E5%BD%A2%E5%9C%B0%E5%9D%80%E4%BC%A0%E9%80%92)\n- [为什么要这样](#%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E8%BF%99%E6%A0%B7)\n  - [基准](#%E5%9F%BA%E5%87%86)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n---\n\n## package.json\n\n``` js\n \"source\": \"lib/index.js\",\n  \"scripts\": {\n    \"build\": \"microbundle\",\n    \"coverage\": \"nyc report --reporter=text-lcov \u003e coverage.lcov \u0026\u0026 codecov\",\n    \"tap\": \"tape -r esm tests/**/*.js | tap-difflet\",\n    \"test\": \"nyc --reporter=text npm run tap\"\n  },\n```\n\n- [microbundle {es6构建小库的0⃣️配置工具}](https://github.com/developit/microbundle)\n\n## index.js\n\n\u003e 很简单, 但是在 关于地址引用 细节方面, 值得推敲 「 Es6语法 」\n\n``` js\nexport default function(source, keys, update) {\n  keys.split \u0026\u0026 (keys = keys.split('.')); // 变成 数组\n\n  let next = copy(source),\n    last = next, // 地址传递\n    i = 0,\n    l = keys.length;\n\n  for (; i \u003c l; i++) {\n    // 每下一层, 顶层地址更换, 子地址相连\n    last = last[keys[i]] =\n      i === l - 1 // 直到 确切最后一层, 运行 update 或 赋值\n        ? !!update.call\n          ? update(last[keys[i]])\n          : update\n        : copy(last[keys[i]]); // \u003c===== 在没到最后一层, 帮 last 换个顶层地址\n  }\n\n  // 因为 最后一层 的 顶层地址, 并没有换, 所以对 last[keys[i]] 的操作\n  // 也是对 next 相同 层级的操作, 🧠\n  return next;\n}\n\nfunction copy(source) {\n  let to = source \u0026\u0026 !!source.pop ? [] : {};\n  for (let i in source) to[i] = source[i];\n  return to;\n}\n\n```\n\n0. 🧠 last 与 next\n\n先地址传递`last = next`, 在没到最后一层时, \n\n``` js\nlast = last[keys[i]] = copy(last[keys[i]])\n```\n\n地址传递后, `last` 与 `next` 相连,  其实可以看成 \n\n``` js\nlast = next[keys[i]] = copy(next[keys[i]])\n```\n\n- **0.1** 可以看出, 是 把 next 的 子地址更换 \u003e `next[keys[i]] = copy(next[keys[i]])`, \n\n\u003e 更换后, 与使用 `cleanSet(current)` current 的相同层级地址 脱离\n\n- **0.2** 同时 新的地址给到 last \u003e `last = next[keys[i]]`\n\n\u003e 所以对 `last` 的 子数据操作, 是会 同时操作 `next`\n\n\n### 疑问copy\n\n#### 两次 copy ?\n\n原本我以为 在第一次 copy 后, \n\n``` js\n  let next = copy(source),\n```\n\n后面 `for 循环`内 的 第二次 `copy`就不需要 \n\n``` js\ncopy(last[keys[i]]);\n```\n\n但是, 不行❌\n\n#### copy的不同\n\n因为 `copy函数` 不同 `Object.assign`\n\n``` js\nfunction copy(source) {\n  let to = source \u0026\u0026 !!source.pop ? [] : {}; // 顶层 不同了\n  for (let i in source) to[i] = source[i]; // 子 依然相连\n  return to;\n}\n```\n\n这个函数只对顶层的 `[]` 或者 `{}` 赋予一个新的地址\n\n而 子数组或子对象 地址依然是与 `source{源}` 子数组或子对象 相连的\n\n`to[i] = source[i];`\n\n## ⚠️ 警告\n\n### 隐形地址传递\n\n从这里可以看出, javascripts 在地址上的,隐形传递\n\n每一层的 `[]` 或者 `{}` 都是有自己的地址的\n\n``` js\nlet first = {second:{}}\nlet b = {}\nb['second'] = first.second\nconsole.log('b.second === first.second',b.second === first.second)\n\nb.second.p = 1\nconsole.log('first.second.p === 1',first.second.p === 1)\n```\n\n\u003e `first` 和 `second` 是 两个不同地址的 对象, 并不是包含关系\n\n\n## 为什么要这样\n\n我想在速度方面, 能告诉你些东西\n\n### 基准\n\n查看 [es bench link](https://esbench.com/bench/5b16f1cbf2949800a0f61cf2) 链接来自己运行基准测试。\n\n注意：YMMV canary 和 firefox dev分别对 对象分配和对象传播 有一些令人印象深刻的改进。\n\n\u003cimg src=\"./assets/chrome_67.png\"\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchinanf-boy%2Fclean-set-explain","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchinanf-boy%2Fclean-set-explain","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchinanf-boy%2Fclean-set-explain/lists"}