⚙️

JavaScript学習:実行環境編|ブラウザ上の実行と非ブラウザ上の実行で何が変わる?

    JavaScript学習記録|なんとなく理解禁止シリーズ

JavaScriptは実行する環境によって使える機能が異なってきますが、そんな話の中でも誰もが触れるであろう「JavaScriptをブラウザ上で実行した時と非ブラウザ上(Node.js環境など)で実行した時で何が変わるか」という話を簡単に整理してみます。

【前提知識】ブラウザとJavaScriptの関係

まずブラウザは、クライアントの要求する情報を提供するために、下記のような様々なコンポーネントを活用しています。

  • ユーザー インターフェース
  • ブラウザ エンジン
  • レンダリング エンジン
  • ネットワーキング
  • UI バックエンド
  • JavaScript インタープリタ(JavaScriptエンジン)
  • データ ストレージ

参照元:ブラウザの仕組み: 最新ウェブブラウザの内部構造|web.dev|記事公開日が2011年8月5日なので古いソースな可能性があります

上記の中のJavaScriptエンジンを扱って、JavaScriptコードの解析と実行を行っていて、ECMAScriptの仕様に則ったJavaScriptのプログラムが扱えたり、DOM APIやFetch APIといったWeb上の様々な操作ができる「Web APIs」が提供され活用できるようになっています。

【補足】実行するブラウザによっても挙動に変化がある

先ほど、ブラウザはJavaScriptエンジンを使ってJavaScriptを実行していると書きましたが、そもそもブラウザによって扱うJavaScriptエンジンが異なっています。

ブラウザJavaScriptエンジン
ChromeV8
SafariJavaScriptCore
FirefoxSpiderMonkey
EdgeChakra / V8
OperaV8

参照元:JavaScriptエンジン|Wikipedia

Chrome / Edge / OperaはV8で動きますが、SafariやFirefoxは扱っているエンジンが異なります。例えば、letの変数制限でもコンテキストによっては、V8ならundefinedになるところSpiderMonkey(Firefox)ではuninitializedとなりエラーを吐く挙動になったります。

まとめ

JavaScriptをブラウザ上で実行するときは、ブラウザが提供しているコンポーネントの内、JavaScriptエンジンを使ってJavaScriptの解析と実行を行っており、その中でECMAScriptに則った機能を扱ったり、Web APIsを活用できたりします。

そして、そのJavaScriptエンジンはブラウザ毎で異なり、別のエンジンを使っているからこその微妙な挙動の違いが出てきます。なので、ES5以前の記述にトランスパイルしたのにブラウザ毎で挙動が異なる場合は、そのブラウザが扱うJavaScriptエンジンの仕様を辿るのが良いでしょう。

【前提知識】Node.js(非ブラウザ環境)とJavaScriptの関係

続いて、Node.jsなどの非ブラウザ環境でJavaScriptを動かすと何が変わってくるのか。まず初めに、Node.jsもV8エンジンを扱ってプログラムの解析と実行を行っており、ブラウザの時と同様ECMAScript仕様のJavaScriptの記述が可能です。

ここで一度、ブラウザ環境と非ブラウザ環境とで扱える機能の違いを簡単に整理しておきます。

実行環境扱える機能
ブラウザECMAScript仕様のJavaScript、Web APIs、ESModules、など
非ブラウザECMAScript仕様のJavaScript、CommonJS(モジュールシステム)、など

当然ですが、Web APIsはあくまでもブラウザ側から提供されているAPIなので、Node.jsでは扱えませんし、逆にCommonJSというモジュールシステムは、Node.jsから提供されているシステムなので、ブラウザ上では扱えません。

エンジニアになって1ヶ月の頃に、ブラウザにアップロードするJavaScriptファイル上に、Node.jsで扱うfsを書いて呆れられた恥ずかしい記憶は頑張って忘れます...笑

【補足】ESModules・CommonJS、モジュールシステムの話

この辺りの話でよく出てくる話が、ESModules・CommonJSとの違い。まずESModulesとは、ES6(2015版)で新たにリリースされたJavaScriptのモジュールシステムです。簡単なモジュールの定義・インポート・実行の例を書いてみます。

const iModule = function () {console.log()};

export { iModule };
import { iModule } from './module.mjs';
iModule();

次にCommonJSとは、Node.jsが採用しているソフトウェア側でJavaScriptを動かすために必要だったモジュールシステムです。こちらも、簡単なモジュールの定義・インポート・実行の例を書いてみます。

引用元:common.js と ECMAScript の違いについて

module.exports = {
  iModule: function () {console.log()}
};
const { iModule } = require('./module.mjs');
iModule();

CommonJSはNode.jsが使っているモジュールシステムなので、当然ブラウザ上では動かす事ができないのですが、Node.jsはECMAScriptの仕様に則ってJavaScriptを解析・実行するため、ESModulesも扱う事ができます。そんな中で、2つのモジュールシステムが混在している問題やどっちを使うの?といった話なんかもあります。もし興味ある人は調べてみると良いかと...!

まとめ

  • ブラウザ上で動くJavaScriptでしか扱えない機能がある(Web APIsやwindowグローバルオブジェクト)
  • ブラウザ毎で扱うJavaScriptエンジンが異なるので、挙動が異なる場合もある
  • Node.jsなど非ブラウザ環境でしか提供されていないモジュールシステムがある(CommonJS)

かなりザックリとした内容でしたが、基本的なところは抑えられたので復習がてらまとめてみました!サーバーサイド開発としては、モジュールシステムの適切な扱い方・管理方法などは、重要なトピックになってくるかと思います!