2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ウェブブラウザでのJavaScriptのグローバル汚染

2
Last updated at Posted at 2026-03-15

はじめに

普段、Javaばかりいじっていて、たまにフロントに触るとあれ?JavaScriptのスコープの範囲って。
忘れてしまうことがある。

で、気にしないとグローバルスコープに定義がされてしまうので、改めて整理し、残しておこうかと。

そもそもグローバルスコープとは?

image.png

グローバル変数とは?

image.png

グローバルオブジェクトは?

image.png

少しまとめ

どこからでもアクセスできるグローバルスコープ。
グローバルスコープで定義されている変数が、グローバル変数。

JavaScriptにおいてのグローバル変数とは、グローバルオブジェクトのプロパティ。

ウェブブラウザでは、例外あれど基本は、Windowがグローバルオブジェクトになる。

つまり、グローバル変数を新たに定義すると、グローバルオブジェクトのプロパティとして定義される。

グローバル汚染とは、このグローバルオブジェクトのプロパティを汚染(=使ってしまう)ってことかな?

そもそもグローバル変数を定義している意識は無いんだけど?

こんなHTMLとJavaScriptを作るだけで、実はグローバル変数を定義してしまっている。

「funcA」と「funcB」はグローバル変数として定義され、グローバルオブジェクトのプロパティとして定義がされている。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Global Scope Test</title>
</head>
<body>
    <h1>ボタンで JavaScript を実行</h1>

    <button type="button" onclick="funcB()">fileB の funcB を実行する</button>

    <hr>

    <script src="fileA.js"></script>
    <script src="fileB.js"></script>

</body>
</html>
function funcA() {
    console.log("fileA.js の funcA が実行されました!");
}
function funcB() {
    if (typeof funcA === "function") {
        funcA();
    }
}

よくよく考えると単純な話。

fileA.jsのfuncAは、fileB.jsのfuncBから呼べる。

「fileB.jsのfuncB → fileA.jsのfuncA」

当たり前と思うのですが、よくよく考えると、ファイルが、「fileB.js」「fileA.js」と分かれているのに、呼べてしまう。

これは、「funcA」と「funcB」がグローバル変数として定義されているから、アクセスが可能となっている。

また、HTMLの「onclick="funcB()"」で、funcBを呼べてしまうのも、グローバル変数に定義されているからと思われる。

    <button type="button" onclick="funcB()">fileB の funcB を実行する</button>

グローバル汚染の何が怖い?

今の例で、初期開発者とは別の方が、「fileC.js」を作り、間違って「funcA」というメソッドを追加してしまった場合。

「funcA」を呼ぶと、「fileA.js」の「funcA」ではなく、「fileC.js」の「funcA」が呼ばれてしまう。

そうすると、「fileA.js」の「funcA」を呼び出していた機能の動きが変わってしまう。

詰まる所、「名前の衝突」

じゃあどうすれば良いの?

IIFE (即時実行関数式)を使った場合

まずは、スコープを正しく整理して、ファイルを整理する。

「fileAのfuncA」は、「fileBのfuncB」からしか呼ばれません。

なので、1つのファイルに集約します。

file.jsというのを作り、1つにまとめる。

function funcA() {
    console.log("fileA.js の funcA が実行されました!");
}

function funcB() {
    if (typeof funcA === "function") {
        funcA();
    }
}

このままだと、ファイルは1個だけど、グローバル変数には両方定義されている。

ここで、「IIFE (即時実行関数式)」を利用する。

image.png

一言で言うと、「funcA」と「funcB」を囲ってしまう。

(function () {
    function funcA() {
        console.log("fileA.js の funcA が実行されました!");
    }

    function funcB() {
        if (typeof funcA === "function") {
            funcA();
        }
    }
})();

こんな感じ。

こうすると、「funcA」「funcB」はグローバル変数に定義がされなくなります。

でも、そうすると、HTMLの「onclick="funcB()"」で、funcBが呼べなくなってしまいます。

image.png

なぜならグローバル変数に定義していないから。

どうするか?というと、

その1)funcBだけ、グローバル変数に定義

(function () {
    function funcA() {
        console.log("fileA.js の funcA が実行されました!");
    }

    function funcB() {
        if (typeof funcA === "function") {
            funcA();
        }
    }
    // funcBのみグローバルに公開
    window.funcB = funcB;
})();

グローバル変数というのは、グローバルオブジェクトのプロパティ。ウィンドウにおいてグローバルオブジェクトは、Window

image.png

というところから、グローバルオブジェクトに明示的に定義したい場合は、「window.プロパティ名=」で定義ができる。

その2)onlickを使わず、イベントリスナーを利用する。

ボタンにIDを付与して、

    <button id="myButton" type="button">fileB の funcB を実行する</button>

即時関数内で「myButton」のクリックイベントに「funcB」を定義する。

(function () {
    function funcA() {
        console.log("fileA.js の funcA が実行されました!");
    }

    function funcB() {
        if (typeof funcA === "function") {
            funcA();
        }
    }

    document.getElementById('myButton').addEventListener('click', funcB);
})();

ES Modules

これは、名前だけ知っていてグローバル汚染の文脈で使ったことなく、Geminiから教わったので早速試す。

IIFE (即時実行関数式)だと、1つのJSファイルが肥大化してしまいそうな気もしたので。

image.png

スクリプトを定義する際に「type="module"」を付与。

<script type="module" src="fileA.js"></script>
<script type="module" src="fileB.js"></script>

funcA側に「export」を付ける。

export function funcA() {
    console.log("fileA.js の funcA が実行されました!");
}

funcB側は、importする。これで「funcA」を「fileB.js」からも呼べるようになる。

import { funcA } from "./fileA.js";

function funcB() {
    // 他のファイルの関数を呼び出す
    if (typeof funcA === "function") {
        funcA();
    }
}

とはいえ、このままだと、HTMLのonclickから「funcB」が呼べません。
「type="module"」を付けた時点で、グローバルスコープに定義されなくなるので。

なので、「window.funcB = funcB;」でグローバルスコープに定義すると、HTMLのonclickからアクセスが可能。

import { funcA } from "./fileA.js";

function funcB() {
    // 他のファイルの関数を呼び出す
    if (typeof funcA === "function") {
        funcA();
    }
}

window.funcB = funcB;

とはいえ、せっかくグローバルスコープを利用しないようにしているので、リスナーイベントを使うのが良さそう。

    <button id="myButton" type="button">fileB の funcB を実行する</button>
import { funcA } from "./fileA.js";

function funcB() {
    // 他のファイルの関数を呼び出す
    if (typeof funcA === "function") {
        funcA();
    }
}

document.getElementById('myButton').addEventListener('click', funcB);

まとめ

なんとなくJavaScirptを書いていたけど、改めて言語化していくと、気になることが沢山出てきてしまった。

Window(頭文字が大文字)とwindow(頭文字が小文字)

あと、「prototype」、「class」

この辺りは、別途深堀理解しながら記事を書くとして。

「IIFE (即時実行関数式)」のみしか知らなかったけど、「ES Modules」を改めて理解。

2
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?