导出调用另一个函数而不是原始函数的新函数有什么意义?

What's the point of exporting a new function that calls another instead of the original?

在探索(为了好玩,手动反编译)一些与 Webpack 捆绑在一起并拆分为多个文件的 JavaScript 代码时,我发现一个模块做了一件奇怪的事情:它创建了一个函数,然后导出了一个调用它创建的函数的函数,而不是仅仅导出第一个函数。

代码的简化、反混淆和反编译版本:

/*
  __d is the function used for registering modules
  It takes in three arguments:
   - a callback, the module's factory
   - the module's ID
   - the dependency map, an array of the IDs of the module's dependencies (which
     is passed in as the last argument of the factory)
*/
__d(
  function(
    window,
    require,
    import_default,
    import_all,
    module,
    exports,
    dependency_map
  ) {
    // Both are regular functions (not arrow functions)
    function example(arg1, arg2) {
      // ...
      // (note that the function does not use `this`)
      // (although it does import another module via `require`)
      return result;
    }

    // Why not just `exports.run_example = example`?
    exports.run_example = function(arg1, arg2) {
      return example(arg1, arg2);
    }
  },
  // And the other two arguments passed into __d (removed because unimportant)
)

这是为什么?不只是导出第一个函数在功能上是等价的吗?

Why is this? Isn't just exporting the first function functionally equivalent?

不完全是,但非常接近。对于您的 example,它确实没有太大区别,因为正如您所说,它没有使用 this 并且因为它使用了两个正式的非剩余参数而不使用 arguments。所以 基本上 等效,抛开狡辩。 :-)

以下是我能看到的差异,这对您的代码可能无关紧要:

  1. example 将不会使用与 run_example 相同的 this 来调用。它将始终获得默认值 this(松散模式下的全局 this,严格模式下的 undefined)。您的 example 不使用 this,因此没有相关差异。

  2. example 总是会恰好得到两个参数,不管 run_example 被调用的次数是多少。由于您的 example 使用两个形式参数并且不使用 arguments 或其余参数,所以这里没有相关差异。

  3. 在大多数现代 JavaScript 引擎上,example 的名称(其 name 属性 以及出现在堆栈跟踪中的内容)是 example; run_example 的名字是 run_example(是的,真的,这是 ES2015 规范中的新内容)。所以你会看到堆栈跟踪的差异,或者如果代码检查导出函数的 name 属性。

  4. 显然,通过 run_example 调用时,多了一个函数调用,因此多了一个堆栈帧。您的代码可能不关心。

这是一个片段,说明了大多数现代引擎上的 1-3 三个:

"use strict";
function example(arg1, arg2) {
    console.log("example:     typeof this? " + typeof this);
    console.log("example:     arguments.length? " + arguments.length);
}
var run_example = function(arg1, arg2) {
    console.log("run_example: typeof this? " + typeof this);
    console.log("run_example: arguments.length? " + arguments.length);
    return example(arg1, arg2);
};

// Call with `this` set to a blank object and no arguments:
run_example.call({});
// Outputs:
// run_example: typeof this? object
// run_example: arguments.length? 0
// example:     typeof this? undefined
// example:     arguments.length? 2

// Call with the default `this` and four arguments:
run_example(1, 2, 3, 4);
// Outputs:
// run_example: typeof this? undefined
// run_example: arguments.length? 4
// example:     typeof this? undefined
// example:     arguments.length? 2

// Function names
console.log("example.name is " + example.name);
console.log("run_example.name is " + run_example.name);
.as-console-wrapper {
    max-height: 100% !important;
}

显示与 ES2015+ rest 参数差异的示例:

"use strict";
function example(...args) {
    console.log("example: args.length? " + args.length);
}
var run_example = function(arg1, arg2) {
    return example(arg1, arg2);
};

// Call with the default `this` and one argument:
run_example(1);
// Outputs:
// example:     args.length? 2

// Whereas calling it directly:
example(1);
// Outputs:
// example: args.length? 1
.as-console-wrapper {
    max-height: 100% !important;
}