at backyard

Color my life with the chaos of trouble.

JavaScriptのimport時のファイル読み込み時、複数から呼ばれたJSファイルの挙動について

JavaScriptを書いていて、挙動を理解していない箇所があるので今日はそのメモを書く。

例えば下記のような3つのファイルがある。

// sample1.js
import { userList } from "./sample2";
import sample3 from "./sample3";

console.log('===> call sample1.js')
console.log("call sample1: ", sample3);

export const getUserNameList = () => {
  return userList.map(({ name }) => name);
};
// sample2.js
import sample3 from "./sample3";

console.log('===> call sample2.js')
console.log("call sample2: ", sample3 );

export const userList = [
  { id: 1, name: "test1" },
  { id: 2, name: "test2" },
  { id: 3, name: "test3" }
];
// sample3.js
const sample3 = "sample3";
console.log("===> call sample3.js!!!!!!!!!!");
export default sample3;

sample1.jssample2.js はどちらも sample3.js を呼び出している。
(より正確には sample1.jssample2.jssample3.js を呼び出しており、さらに呼び出された sample2.jssample3.js を呼び出す形となっている)

そしてこのファイルのうち、 sample1.js は下記のコードから読みこまれている。
(create-react-app を使って生成したプロジェクト内で利用している。実際のコードはGitHubに置いておく)

import logo from './logo.svg';
import './App.css';
import { getUserNameList } from './samples/sample1';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
        <ul>
          {getUserNameList().map((userName, index) => (
            <li key={index}>{userName}</li>
          ))}
        </ul>
      </header>
    </div>
  );
}

export default App;

一見すると、sample3.js は2回呼び出されているので、下記の処理も2回動きそうにも思えるが、このログは1度しか表示されない。

console.log("===> call sample3.js!!!!!!!!!!");

下記が実際に動かした際の開発者ツールのキャプチャ。

f:id:shinshin86:20211008071531p:plain
sample3.jsの処理が動いているのはログを見る限り一度だけ

なお、ブラウザは下記の環境で実行している。

Google Chrome    94.0.4606.71 (Official Build) (arm64)

複数箇所から参照されているファイルは一度読まれると、内部のメモリ(この場合、ブラウザ内のメモリ)でその情報を持つようになっているのだろうか?

そこがよくわからない。分かり次第こちらに追記したいと考えている。

追記:依存関係グラフに属する各モジュールは葉から順に(帰りがけ順で)実行される?

Top level awaitについて調べていたときに出会ったこちらのポスト

qiita.com

内容としてはtop level awaitやESModuleについての話だが、その中に下記のような記述がある。

具体的な実行順は、依存関係グラフを深さ優先探索することで決められます。依存関係グラフに属する各モジュールは葉から順に(帰りがけ順で)実行されます。ただし、同じモジュールを訪れるのは1回だけです。

これが今回の事例にも当てはまる、と仮定するなら挙動としてはこれと同じ挙動をとっていることになる。

今回のコードも依存関係の深い順から記載していくと sample3.js -> sample2.js -> sample1.js という順となるからだ。
この依存家計を元に帰りがけ順に実行されていくとするならば、この挙動も納得がいく。

ただ、上のポストはop level awaitやESModuleについての話なので、もしかしたら今ここで書いていることとは何も無関係の可能性もある。

が、一応備忘録としてこちらに残しておく。

実際のコード

実際のコードはGitHubに置いてある。

github.com