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. ...
相关推荐
2601_962218473 小时前
万象生鲜系统区块链溯源技术帮助生鲜企业搭建食品安全数字化体系
大数据·运维·微服务·云原生·架构
ZGIAI4 小时前
ZGI Workflow:条件分支走错时先查哪一层
人工智能·架构
ZGIAI4 小时前
ZGI 文件产物:生成报告后怎样交付
人工智能·架构
新时代牛马4 小时前
Linux 内核入门地图:架构、源码目录与五大子系统
linux·运维·架构
智购科技无人售货机厂家4 小时前
2026自动售货机制冷系统维护指南:从散热器清洁到压缩机换油的工程实践~YH
运维·redis·物联网·缓存·架构
海兰4 小时前
【应用】基于 Next.js 16 + Python mplfinance的金融K线图与技术指标可视化平台(三)
javascript·python·金融
wangruofeng4 小时前
Phosphor Icons 官网源码拆解:URL 即存储、水波动画与 7362 Star 图标库的架构实践
前端·架构·github
wangruofeng5 小时前
AI 画架构图总差点意思,archify 给它加了一条验收流水线
架构·github·aigc
叶落方知秋6 小时前
大模型学习笔记:排序怎么帮公司赚钱、多方怎么博弈
架构
小磊哥er6 小时前
深入解构Claude Code - 第 7 篇 · 连接外面的世界
javascript·ai编程