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. ...
相关推荐
浪裡遊18 分钟前
React Hooks全面解析:从基础到高级的实用指南
开发语言·前端·javascript·react.js·node.js·ecmascript·php
ai小鬼头2 小时前
AIStarter如何助力用户与创作者?Stable Diffusion一键管理教程!
后端·架构·github
Liudef062 小时前
2048小游戏实现
javascript·css·css3
掘金-我是哪吒4 小时前
分布式微服务系统架构第156集:JavaPlus技术文档平台日更-Java线程池使用指南
java·分布式·微服务·云原生·架构
国服第二切图仔4 小时前
文心开源大模型ERNIE-4.5-0.3B-Paddle私有化部署保姆级教程及技术架构探索
百度·架构·开源·文心大模型·paddle·gitcode
独立开阀者_FwtCoder4 小时前
【Augment】 Augment技巧之 Rewrite Prompt(重写提示) 有神奇的魔法
前端·javascript·github
我想说一句4 小时前
事件机制与委托:从冒泡捕获到高效编程的奇妙之旅
前端·javascript
汤姆Tom4 小时前
JavaScript reduce()函数详解
javascript
小飞悟4 小时前
你以为 React 的事件很简单?错了,它暗藏玄机!
前端·javascript·面试
中微子4 小时前
JavaScript 事件机制:捕获、冒泡与事件委托详解
前端·javascript