web-dev-qa-db-ja.com

ES6モジュール:インポートされた定数は最初は未定義です。彼らは後で利用可能になります

JavaScriptアプリケーションでES6モジュールを使用しています。ソースはwebpackとbabelでコンパイルされています。これは私に問題を引き起こすファイルの短縮版です:

export const JUST_FORM = 0;
export const AS_PAGE = 1;

console.log(AS_PAGE); // **

export default function doSomething(mode = AS_PAGE) {
  console.log(mode);
  console.log(JUST_FORM);
}

私はあなたが期待するようにこの機能を使用します。

import doSomething, { AS_PAGE } from './doSomething'

console.log(AS_PAGE);

doSomething();

アプリを実行すると、undefinedが3回出力され、期待値AS_PAGEconsole.logでマークされた**が1回だけ出力されます。しかし、これは最後に印刷されます!それはそれを示しています:

  • AS_PAGE定数は、doSomething関数 `のデフォルトパラメータとして使用される場合、関数を定義する時点では定義されていません。
  • doSomethingが呼び出されたとき、JUST_FORM定数は定義されていません。
  • AS_PAGE定数は、明示的にインポートされた場合は定義されません。

どうやら、ここで起こっていることは、defaultエクスポートのみが解析および評価され、ファイルの残りの部分は後で無視されるということです。このファイルをアプリのいくつかの異なる場所(現時点では非常に大きい)にインポートし、ある時点でそれらの値が実際に使用可能になります。コンソール出力から判断すると、それは時間の問題ですが、別の理由がある可能性があります。もちろん、インポートはすべての場所でまったく同じ方法で行います。

とにかく、何かをインポートするとすぐに使用可能になり、コードで使用できるという前提で、アプリケーション全体を記述しました。 ES6モジュールがどのように機能するかについて(簡単に)読みましたが、この仮定が間違っていることを証明するものは何も見つかりませんでした。そして、それは今まで働いてきました。

また、webpack-dev-serverを指定して実行したり、単一のバンドルにコンパイルしたりしても、動作は同じです。

この動作は本当に正しいですか?何が原因でしょうか?

28
tobik

コメントで提案されているように、ここでの答えは循環依存です。

質問で提供されたコードには循環依存関係は実際にはありませんが(コードの単なるスニペットであるため)、症状は非常に明確です。

循環依存関係の最も単純な例は、ファイルAがファイルBをインポートし、ファイルBがAをインポートする場合です。残念ながら、この問題を検出するのが難しいのは、円が非常に大きく、大量のファイルにまたがることがあることです。

ES6では循環依存関係がサポートされており、十分に注意して使用できます。ただし、ここでの私の留意点は、循環依存は非常に多くの場合、設計上の誤った決定の兆候であるということです。それはまさに私のケースでした。

33
tobik