为什么巴别塔要重写导入函数调用(0, FN)(…)?

在前端开发中,模块化已经成为了一种主流的开发方式。而在模块化的实现过程中,模块导入导出是一个关键的环节。ES6 的模块系统引入了 importexport 语句用于模块的导入和导出。而在早期的模块规范中,CommonJS 规范使用的是 requiremodule.exports。这两种模块规范虽然在不同的环境下得到广泛应用,但它们的实现方式存在一些差异。为了兼容各种模块规范,在 Webpack 等工具中,通常会对模块导入导出进行一定的转换。

在 Babel 中,有一个插件 @babel/plugin-transform-modules-commonjs,它可以将 ES6 模块语法转换为 CommonJS 规范的代码。但是,这个插件默认生成的代码包含了一个奇怪的函数调用:(0, _foo.default)(...),其中 _foo 是被导入的模块名,.default 表示导出的默认值。这个函数调用看起来很奇怪,让人不禁想问,为什么要这样写呢?

函数调用的奥秘

要理解为什么要这样写,首先需要了解 JavaScript 中的函数调用机制。JavaScript 中的函数调用有两种方式:作为方法调用和作为函数调用。

作为方法调用时,函数体内的 this 关键字会指向调用该方法的对象。比如:

----- --- - -
  ----- --------
  --------- -
    -----------------------
  -
--

-------------- -- -- -------

而作为函数调用时,函数体内的 this 关键字会指向全局对象(在浏览器中是 window 对象,在 Node.js 中是 global 对象)。比如:

-------- --------- -
  -----------------------
-

---------- -- ---------- ------ ---- -------- ------ -- ---------

因此,如果我们想让函数作为方法调用时能够正确地获取到 this,就必须使用 .call().apply() 来指定 this 的值。比如:

-------- --------- -
  -----------------------
-

----- --- - -
  ----- -------
--

------------------ -- -- -------

回到 (0, _foo.default)(...) 这个奇怪的函数调用,它其实是一种巧妙的函数绑定方式。(0, _foo.default) 实际上是一个 IIFE(Immediately Invoked Function Expression),它执行后返回 _foo.default 函数本身。而 (0, ...) 这个语法则是 JavaScript 中的逗号运算符,它会返回最后一个操作数的值。因此,整个表达式的值就是 _foo.default 函数本身。

这种写法之所以有用,是因为它可以将函数绑定到全局对象上,从而避免在 CommonJS 规范中出现丢失 this 的问题。具体来说,当使用 CommonJS 规范导入一个模块时,该模块的代码会运行在一个单独的作用域中,与当前模块的作用域是相互独立的。而在模块代码中使用 this 关键字时,它实际上指向的是模块内部的作用域,而不是全局作用域。因此,在 CommonJS 规范中,如果想要将一个函数作为方法调用调用,并且

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/12771