cloneDeep 可以拷贝模块吗?

1. 问题描述

想要进行深拷贝,最好的选择莫过于使用 lodash.cloneDeep 方法了。那么你知道 lodash.cloneDeep 可以深拷贝模块吗?

请看下面的代码:

js 复制代码
// main.js
export const a1 = {
  name: "a1",
}

export const a2 = {
  name: "a2",
}

export const a3 = {
  name: "a3",
}

// 入口文件
// index.js
import * as mainModule from './main.js';
import { cloneDeep } from 'lodash-es'

console.log(mainModule);
console.log(cloneDeep(mainModule))

有两个文件,第一个main.js,第二个为入口文件index.js。如果用 webpack@5 打包,然后在浏览器内执行,能拷贝成功吗?

2. 答案及解析

如果你说不能,恭喜你答对了。下图为打包之后在浏览器里的执行结果:

记住:这种图很重要!!!

如果打断点调试一下cloneDeep源码,很快就会找到原因:

js 复制代码
// 获取tag
const tag = getTag(value)

if (tag == objectTag /* '[object Object]' */ || 
    tag == argsTag /* '[object Arguments]' */ || 
    (isFunc && !object)) {
} else {
  if (!cloneableTags[tag]) {  
    // 如果不符合可拷贝tag,返回 {}
    return object ? value : {};
  }
  result = initCloneByTag(value, tag, isDeep);
}

这里getTag的逻辑也很简单,如果当前对象中存在 Symbol(Symbol.toStringTag),取该值;如果不存在,则取 Object.prototype.toString.call(obj)

所以这里的tag值为"[object Module]"。如果查看 cloneableTags 就会发现,

ts 复制代码
[ object  Arguments ] : true
[ object  ArrayBuffer ] : true
[ object  Array ] : true
[ object  Boolean ] : true
[ object  DataView ] : true
[ object  Date ] : true
[ object  Error ] : false
[ object  Float32Array ] : true
[ object  Float64Array ] : true
[ object  Function ] : false
[ object  Int8Array ] : true
[ object  Int16Array ] : true
[ object  Int32Array ] : true
[ object  Map ] : true
[ object  Number ] : true
[ object  Object ] : true
[ object  RegExp ] : true
[ object  Set ] : true
[ object  String ] : true
[ object  Symbol ] : true
[ object  Uint8Array ] : true
[ object  Uint8ClampedArray ] : true
[ object  Uint16Array ] : true
[ object  Uint32Array ] : true
[ object  WeakMap ] : false

很明显,"[object Module]" 是不能深拷贝的,所以直接返回了 {}

3. 再多些疑问

3.1 webpack@3打包之后的资源可以执行成功吗?

如果你使用 webpack@3 使用同样的配置去打包这段代码,然后再在浏览器中执行打包好的代码,你会发现神奇的事发生了:

是的,你没看错,webpack@3打包成功了。从上图能看到,这里的模块,并没有 @@toStringTag 属性,此时返回的 tag"[ object Object ]",所以成功执行了深度拷贝。

3.2 webpack3之后发生了什么?

下面是 v3v4或者v5 里打包运行时对模块的处理,可以看出,后者定义值为 ModuleSymbol.toStringTag属性。

那为什么要这么做呢?下面是我的个人理解:

ES6中的模块机制是未来标准。标识模块 ,在webpack内部处理机制中作为一种暗示。这样做有很多好处:

  1. 对齐标准。也可以区分 ES ModulesCommonjs
  2. 基于 ES6 标准,webpack可以做tree-shaking等优化。
  3. 标准中提供了元数据,import.meta...等等,也可以在模块处理中发挥作用。
  4. ...
相关推荐
We་ct2 小时前
LeetCode 5. 最长回文子串:DP + 中心扩展
前端·javascript·算法·leetcode·typescript
zandy10116 小时前
Agentic BI 架构实战:当AI Agent接管数据建模、指标计算与可视化全链路
人工智能·架构
薪火铺子8 小时前
微服务认证方案对比与选型
微服务·云原生·架构
运维全栈笔记9 小时前
K8S部署Redis高可用全攻略:1主2从3哨兵架构实战
redis·docker·云原生·容器·架构·kubernetes·bootstrap
cn_mengbei10 小时前
用React Native开发OpenHarmony应用:Reanimated共享元素过渡
javascript·react native·react.js
kyriewen10 小时前
前端测试:别为了100%覆盖率而写测试,那是自欺欺人
前端·javascript·单元测试
Data_Journal10 小时前
如何使用cURL更改User Agent
大数据·服务器·前端·javascript·数据库
掌心向暖RPA自动化10 小时前
如何获取网页某个元素在屏幕可见部分的中心坐标影刀RPA懒加载坐标定位技巧
java·javascript·自动化·rpa·影刀rpa
竹林81811 小时前
wagmi v2 多链钱包切换:一个 Uniswap 仿盘项目让我踩了三天坑
前端·javascript
weixin_4462608511 小时前
城市智能化的底层基石:基于腾讯地图服务生态的移动定位与导航架构指引
大数据·人工智能·架构