{"componentChunkName":"component---src-templates-blog-post-js","path":"/deep-clone/","result":{"data":{"markdownRemark":{"html":"<h2>JS 数据类型</h2>\n<p>开头先来复习下 JS 都有哪些数据类型</p>\n<p>Undefined、Boolean、String、Number、Null、Symbol、BigInt 8 个基本类型和 1 个 Object</p>\n<p>注意: 函数也是具有额外可调用能力的对象</p>\n<!-- set, map, weakset, weakmap? 以及一些新语法 -->\n<h2>思路</h2>\n<p>实现深拷贝可以进行递归, 每一层都对数据类型做判断:</p>\n<ul>\n<li>如果是基本类型, 直接复制</li>\n<li>obj 进行重建</li>\n</ul>\n<p>代码实现时注意:</p>\n<ul>\n<li>代码层级过深时会爆栈(这里可以使用尾递归优化)</li>\n<li>存在循环引用问题怎么处理</li>\n<li>引用丢失(两个对象引用同一个内存)</li>\n<li>set, map, weakset, weakmap 以及一些新语法</li>\n</ul>\n<p>进阶: 不用递归如何使用循环来求解</p>\n<h2>代码实现</h2>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const</span> objectTag <span class=\"token operator\">=</span> <span class=\"token string\">\"[object Met]\"</span>\n<span class=\"token keyword\">const</span> arrayTag <span class=\"token operator\">=</span> <span class=\"token string\">\"[object Met]\"</span>\n<span class=\"token keyword\">const</span> argsTag <span class=\"token operator\">=</span> <span class=\"token string\">\"[object Arguments]\"</span>\n<span class=\"token keyword\">const</span> mapTag <span class=\"token operator\">=</span> <span class=\"token string\">\"[object Map]\"</span>\n<span class=\"token keyword\">const</span> setTag <span class=\"token operator\">=</span> <span class=\"token string\">\"[object Set]\"</span>\n\n<span class=\"token keyword\">const</span> boolTag <span class=\"token operator\">=</span> <span class=\"token string\">\"[object Boolean]\"</span>\n<span class=\"token keyword\">const</span> stringTag <span class=\"token operator\">=</span> <span class=\"token string\">\"[object String]\"</span>\n<span class=\"token keyword\">const</span> numberTag <span class=\"token operator\">=</span> <span class=\"token string\">\"[object Number]\"</span>\n<span class=\"token keyword\">const</span> dateTag <span class=\"token operator\">=</span> <span class=\"token string\">\"[object Date]\"</span>\n<span class=\"token keyword\">const</span> errorTag <span class=\"token operator\">=</span> <span class=\"token string\">\"[object Error]\"</span>\n\n<span class=\"token keyword\">const</span> regTag <span class=\"token operator\">=</span> <span class=\"token string\">\"[object RegExp]\"</span>\n<span class=\"token keyword\">const</span> FunTag <span class=\"token operator\">=</span> <span class=\"token string\">\"[object Function]\"</span>\n\n<span class=\"token comment\">// 👇es 6 后无包装类型</span>\n<span class=\"token comment\">// const symbolTag = \"[object Symbol]\"</span>\n<span class=\"token comment\">// const bigintTag = \"[object BigInt]\"</span>\n\n<span class=\"token comment\">// const nullTag = \"[object Null]\"</span>\n<span class=\"token comment\">// const undefinedTag = \"[object Undefined]\"</span>\n\n<span class=\"token keyword\">const</span> deepTag <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span>mapTag<span class=\"token punctuation\">,</span> setTag<span class=\"token punctuation\">,</span> objectTag<span class=\"token punctuation\">,</span> arrayTag<span class=\"token punctuation\">,</span> argsTag<span class=\"token punctuation\">]</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">forEach</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">array<span class=\"token punctuation\">,</span> iteratee</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">let</span> index <span class=\"token operator\">=</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span>\n  <span class=\"token keyword\">while</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">++</span>index <span class=\"token operator\">&lt;</span> array<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">iteratee</span><span class=\"token punctuation\">(</span>array<span class=\"token punctuation\">[</span>index<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> index<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">return</span> array\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">cloneFunction</span> <span class=\"token operator\">=</span> <span class=\"token parameter\">func</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> bodyReg <span class=\"token operator\">=</span> <span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">(?&lt;={)(.|\\n)+(?=})</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">m</span></span>\n  <span class=\"token keyword\">const</span> paramReg <span class=\"token operator\">=</span> <span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">(?&lt;=\\().+(?=\\)\\s+{)</span><span class=\"token regex-delimiter\">/</span></span>\n\n  <span class=\"token keyword\">const</span> funcString <span class=\"token operator\">=</span> func<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>func<span class=\"token punctuation\">.</span>prototype<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> param <span class=\"token operator\">=</span> paramReg<span class=\"token punctuation\">.</span><span class=\"token function\">exec</span><span class=\"token punctuation\">(</span>funcString<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">const</span> body <span class=\"token operator\">=</span> bodyReg<span class=\"token punctuation\">.</span><span class=\"token function\">exec</span><span class=\"token punctuation\">(</span>funcString<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>body<span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>param<span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Function</span><span class=\"token punctuation\">(</span>body<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">const</span> args <span class=\"token operator\">=</span> param<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">\",\"</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Function</span><span class=\"token punctuation\">(</span><span class=\"token operator\">...</span>args<span class=\"token punctuation\">,</span> body<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 箭头函数没有 prototype</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">eval</span><span class=\"token punctuation\">(</span>funcString<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">cloneOtherType</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">target<span class=\"token punctuation\">,</span> type</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> Ctor <span class=\"token operator\">=</span> target<span class=\"token punctuation\">.</span>constructor\n\n  <span class=\"token keyword\">switch</span> <span class=\"token punctuation\">(</span>type<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">case</span> boolTag<span class=\"token operator\">:</span>\n    <span class=\"token keyword\">case</span> stringTag<span class=\"token operator\">:</span>\n    <span class=\"token keyword\">case</span> numberTag<span class=\"token operator\">:</span>\n    <span class=\"token keyword\">case</span> dateTag<span class=\"token operator\">:</span>\n    <span class=\"token keyword\">case</span> errorTag<span class=\"token operator\">:</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Ctor</span><span class=\"token punctuation\">(</span>target<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">case</span> regTag<span class=\"token operator\">:</span>\n      <span class=\"token keyword\">const</span> re <span class=\"token operator\">=</span> <span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">\\w*$</span><span class=\"token regex-delimiter\">/</span></span>\n      <span class=\"token keyword\">const</span> reg <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">target<span class=\"token punctuation\">.</span>constructor</span><span class=\"token punctuation\">(</span>target<span class=\"token punctuation\">.</span>source<span class=\"token punctuation\">,</span> re<span class=\"token punctuation\">.</span><span class=\"token function\">exec</span><span class=\"token punctuation\">(</span>target<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n      reg<span class=\"token punctuation\">.</span>lastIndex <span class=\"token operator\">=</span> target<span class=\"token punctuation\">.</span>lastIndex\n      <span class=\"token keyword\">return</span> reg\n    <span class=\"token keyword\">case</span> FunTag<span class=\"token operator\">:</span>\n      <span class=\"token keyword\">return</span> <span class=\"token function\">cloneFunction</span><span class=\"token punctuation\">(</span>target<span class=\"token punctuation\">)</span>\n    <span class=\"token comment\">// Symbols 以及没有包装类型了, 面试的时候可以提一下可以用 Object.getOwnPropertySymbols() copy symbol</span>\n    <span class=\"token keyword\">default</span><span class=\"token operator\">:</span>\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">const</span> deepClone <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>target<span class=\"token punctuation\">,</span> map <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">WeakMap</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token punctuation\">(</span><span class=\"token keyword\">typeof</span> target <span class=\"token operator\">!==</span> <span class=\"token string\">\"object\"</span> <span class=\"token operator\">&amp;&amp;</span> <span class=\"token keyword\">typeof</span> target <span class=\"token operator\">!==</span> <span class=\"token string\">\"function\"</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">||</span>\n    target <span class=\"token operator\">===</span> <span class=\"token keyword\">null</span>\n  <span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> source\n\n  <span class=\"token keyword\">const</span> type <span class=\"token operator\">=</span> <span class=\"token class-name\">Object</span><span class=\"token punctuation\">.</span>prototype<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">.</span><span class=\"token function\">call</span><span class=\"token punctuation\">(</span>target<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">let</span> cloneTarget\n\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>deepTag<span class=\"token punctuation\">.</span><span class=\"token function\">includes</span><span class=\"token punctuation\">(</span>type<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> <span class=\"token function\">cloneOtherType</span><span class=\"token punctuation\">(</span>target<span class=\"token punctuation\">,</span> type<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">const</span> Ctor <span class=\"token operator\">=</span> target<span class=\"token punctuation\">.</span>constructor\n  cloneTarget <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Ctor</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token comment\">// 处理循环引用</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>map<span class=\"token punctuation\">.</span><span class=\"token function\">has</span><span class=\"token punctuation\">(</span>target<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> map<span class=\"token punctuation\">.</span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span>target<span class=\"token punctuation\">)</span>\n  map<span class=\"token punctuation\">.</span><span class=\"token function\">set</span><span class=\"token punctuation\">(</span>target<span class=\"token punctuation\">,</span> cloneTarget<span class=\"token punctuation\">)</span>\n\n  <span class=\"token comment\">// 处理 set</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>type <span class=\"token operator\">===</span> setTag<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    target<span class=\"token punctuation\">.</span><span class=\"token function\">forEach</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">value</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n      cloneTarget<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token function\">deepClone</span><span class=\"token punctuation\">(</span>value<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> cloneTarget\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// 处理 map</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>type <span class=\"token operator\">===</span> mapTag<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    target<span class=\"token punctuation\">.</span><span class=\"token function\">forEach</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">value<span class=\"token punctuation\">,</span> key</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n      cloneTarget<span class=\"token punctuation\">.</span><span class=\"token function\">set</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">,</span> <span class=\"token function\">deepClone</span><span class=\"token punctuation\">(</span>value<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> cloneTarget\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">const</span> keys <span class=\"token operator\">=</span> arrayTag <span class=\"token operator\">?</span> <span class=\"token keyword\">undefined</span> <span class=\"token operator\">:</span> Object<span class=\"token punctuation\">.</span><span class=\"token function\">keys</span><span class=\"token punctuation\">(</span>target<span class=\"token punctuation\">)</span>\n\n  <span class=\"token function\">forEach</span><span class=\"token punctuation\">(</span>keys <span class=\"token operator\">||</span> target<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">value<span class=\"token punctuation\">,</span> key</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>keys<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      key <span class=\"token operator\">=</span> value\n    <span class=\"token punctuation\">}</span>\n    cloneTarget<span class=\"token punctuation\">[</span>key<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">deepClone</span><span class=\"token punctuation\">(</span>target<span class=\"token punctuation\">[</span>key<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> map<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> cloneTarget\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h2>延伸问题</h2>\n<ul>\n<li>\n<p>如果使用 JSON.stringify(JSON.parse(xx)) 来进行深拷贝会有什么问题?</p>\n<ul>\n<li>丢失循环引用信息</li>\n<li>丢失正则和函数等特殊对象类型</li>\n<li>丢失原型链 等特殊值</li>\n<li>丢失 undefined</li>\n</ul>\n</li>\n<li>\n<p><strong>proto</strong> 和 prototype 什么区别</p>\n<ul>\n<li><strong>proto</strong> 指向构造函数的原型</li>\n<li>prototype 是仅在函数对象上存在的属性，用于定义通过构造函数创建的对象的原型链。</li>\n<li><strong>注意</strong>: for...in 还会枚举继承的属性，用这种遍历方法需要搭配 Object.hasOwnProperty()</li>\n</ul>\n</li>\n<li>\n<p>类数组对象是什么</p>\n<ul>\n<li>JavaScript 类型化数组是一种类似数组的对象，并提供了一种用于在内存缓冲中访问原始二进制数据的机制。</li>\n</ul>\n</li>\n<li>\n<p>包装类型和 x 是什么</p>\n<ul>\n<li>引用类型和包装类型的主要区别就是对象的生存期，使用 new 操作符创建的引用类型的实例，在执行流离开当前作用域之前都一直保存在内存中，而自基本类型则只存在于一行代码的执行瞬间，然后立即被销毁，这意味着我们不能在运行时为基本类型添加属性和方法。</li>\n<li>围绕原始数据类型创建一个显式包装器对象从 ECMAScript 6 开始不再被支持, 所以诸如 Symbol BigInt 没有包装类型\n<ul>\n<li>诸如 new String() 等因为历史的原因留了下来</li>\n<li>对于 Symbol 可以用 Object.getOwnPropertySymbols() copy</li>\n</ul>\n</li>\n</ul>\n</li>\n<li>\n<p>while、for...in、for 效率哪个高?</p>\n<ul>\n<li>while、for 要比其他循环要高，优势是不用执行回调函数等</li>\n</ul>\n</li>\n</ul>","timeToRead":3,"frontmatter":{"title":"一文了解深拷贝","date":"December 21, 2023","spoiler":null},"fields":{"slug":"/deep-clone/"}}},"pageContext":{"slug":"/deep-clone/","previous":{"fields":{"slug":"/learn-browser/render/"},"frontmatter":{"title":"渲染流程"}},"next":{"fields":{"slug":"/color-transparency-hexadecimal-comparison-table/"},"frontmatter":{"title":"颜色透明度16进制对照表"}}}},"staticQueryHashes":["3649515864","63159454"]}