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. ...
相关推荐
前端Hardy9 分钟前
HTML&CSS:有趣的SVG路径动画效果
javascript·css
前端Hardy13 分钟前
HTML&CSS:超酷炫的3D动态卡片
前端·javascript·css
江城开朗的豌豆1 小时前
我在项目中这样处理useEffect依赖引用类型,同事直呼内行
前端·javascript·react.js
听风的码1 小时前
Vue2封装Axios
开发语言·前端·javascript·vue.js
白嫖叫上我1 小时前
js如何循环HTMLCollection
javascript
卷卷卷土重来1 小时前
C++单例模式
javascript·c++·单例模式
危险库1 小时前
单例模式:确保一个类只有一个实例【设计模式】
javascript·单例模式·设计模式
程序员不迷路2 小时前
微服务学习
微服务·架构
素界UI设计2 小时前
建筑行业变革:用Three.js构建BIM数据可视化孪生平台
开发语言·javascript·信息可视化
Sadsvit2 小时前
源码编译安装LAMP架构并部署WordPress(CentOS 7)
linux·运维·服务器·架构·centos