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. ...
相关推荐
炫饭第一名12 小时前
速通Canvas指北🦮——基础入门篇
前端·javascript·程序员
进击的尘埃14 小时前
Vue3 响应式原理:从 Proxy 到依赖收集,手撸一个迷你 reactivity
javascript
willow14 小时前
JavaScript数据类型整理1
javascript
LeeYaMaster14 小时前
20个例子掌握RxJS——第十一章实现 WebSocket 消息节流
javascript·angular.js
UIUV15 小时前
RAG技术学习笔记(含实操解析)
javascript·langchain·llm
ray_liang15 小时前
用六边形架构与整洁架构对比是伪命题?
java·架构
Java编程爱好者16 小时前
字节二面:被问“大模型知识过时了怎么解?”,我答“微调”,面试官当场黑脸:“听说过 RAG 吗?”
架构
颜酱16 小时前
理解二叉树最近公共祖先(LCA):从基础到变种解析
javascript·后端·算法
FansUnion17 小时前
我如何用 Next.js + Supabase + Cloudflare R2 搭建壁纸销售平台——月成本接近 $0
javascript
左夕18 小时前
分不清apply,bind,call?看这篇文章就够了
前端·javascript