来自交叉依赖的默认参数未定义

Default parameters from cross dependency is undefined

我有两个相互依赖的文件:

runner.js

import { build, altBuild } from './build';

const defaultValue = {
  code: 42,
};

export function options(value = defaultValue) {
  return value;
}

export function altOptions(value = { code: 9 }) {
  return value;
}

console.log(build());
console.log(altBuild());

build.js

import { options, altOptions } from './runner';

const buildParams = {
  foo: 'bar',
  extra: options(),
}

const altBuildParams = {
  foo: 'bar',
  extra: altOptions(),
}

export function build() {
  console.log(options());
  return buildParams;
}

export function altBuild() {
  console.log(altOptions());
  return altBuildParams;
}

当 运行ning 时,我希望输出为:

{ code: 42 }
{ foo: 'bar', extra: { code: 42 } }
{ code: 9 }
{ foo: 'bar', extra: { code: 9 } }

但它是:

{ code: 42 }
{ foo: 'bar', extra: undefined }
{ code: 9 }
{ foo: 'bar', extra: { code: 9 } }

我可以解决这个问题,但我真的很好奇为什么会这样。我正在使用 Node 7.10 和 babel-node 6.26 来 运行 这个例子。


使用转译后的代码更新:

转译runner.js

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.options = options;
exports.altOptions = altOptions;

var _build = require('./build');

var defaultValue = {
  code: 42
};

function options() {
  var value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultValue;

  return value;
}

function altOptions() {
  var value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { code: 9 };

  return value;
}

console.log((0, _build.build)());
console.log((0, _build.altBuild)());

转译build.js

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.build = build;
exports.altBuild = altBuild;

var _runner = require('./runner');

var buildParams = {
  foo: 'bar',
  extra: (0, _runner.options)()
};

var altBuildParams = {
  foo: 'bar',
  extra: (0, _runner.altOptions)()
};

function build() {
  console.log((0, _runner.options)());
  return buildParams;
}

function altBuild() {
  console.log((0, _runner.altOptions)());
  return altBuildParams;
}

在循环依赖中,虽然函数 声明 可以立即用于所有其他需要初始化顺序的东西。这不会按需发生(惰性),而是按导入顺序定义,然后评估完整的模块主体。

在你的情况下,取决于首先需要哪个文件,你可以

  1. 得到 defaultValue 初始化对象
  2. 调用 build(),得到 options() 就好了,然后引用未初始化的 buildParams,并记录结果
  3. 调用 build(),得到 altOptions() 就好了,然后引用未初始化的 altBuildParams,并记录结果
  4. get buildParamsoptions() 初始化得到 defaultValue
  5. altOptions()
  6. 初始化altBuildParams

  1. get buildParams initialised with options() 指的是未初始化的 defaultValue
  2. altOptions()
  3. 初始化altBuildParams
  4. 得到 defaultValue 初始化对象
  5. 调用 build(),获取 options() 并使用 buildParams,并记录结果
  6. 调用 build(),获取 altOptions() 并使用 altBuildParams,并记录结果

在任何一种情况下,你都引用了一个未初始化的变量,它会在真正的 ES6 中抛出异常,但在 babel 转换中只会以 undefined 结束。你无法完成这项工作。

一般来说,只在有循环依赖的模块中使用函数声明,不要在模块顶层范围内调用任何东西(包括变量初始化)。确保代码至少可以放在一个大文件中。