In JavaScript, the global scope is the broadest level of access where variables and functions defined outside of any specific function or block can be referenced and modified from anywhere in the program.
JavaScriptにおける「スコープ(scope)」とは、変数や関数がアクセス可能な範囲のことを指します。スコープを正しく理解することで、意図した箇所で変数を使用できるように管理し、予期せぬ変数の上書きや衝突を防止できます。
このページでは、スコープの中でも特に「グローバルスコープ (Global Scope)」にフォーカスしています。
グローバルスコープは、JavaScriptコードのどの部分からでも直接アクセスが可能な最も広い範囲のスコープです。ブラウザでは「window
」オブジェクト(またはself
、frames
など)が、Node.jsなどの環境では「global
」オブジェクトがグローバルオブジェクトとして存在しています。最新の仕様(ECMAScript 2020以降)では、環境に依存しないグローバルオブジェクトとして「globalThis
」が用意されています。
window
、self
、globalThis
global
、globalThis
self
、globalThis
JavaScriptでは、グローバルスコープに定義された変数のことをグローバル変数と呼びます。グローバル変数は、プログラム内のどこでも参照・変更が可能です。
例:グローバル変数の宣言
JavaScript
var globalVar = "Hello, World!";
function showGlobalVar() {
console.log(globalVar); // "Hello, World!"
}
showGlobalVar();
console.log(globalVar); // 関数の外でもアクセス可能
このようにvar
キーワードで宣言された変数は、関数やブロックの外側(トップレベル)で宣言されるとグローバルスコープに属します。
グローバルスコープに置いた変数や関数は、アプリケーションのあらゆる場所からアクセスできるため、利便性が高いというメリットがあります。ただし、同時に「どこからでも変更可能」という意味でもあるため、変数の衝突や上書きが起こりやすい側面も存在します。
多数のモジュールや関数が共通して利用する定数や処理をグローバルスコープに配置することで、使い回しがしやすくなる場合もあります。しかし、複雑化したプロジェクトでは、グローバルスコープに要素を増やしすぎると保守が難しくなるため、注意が必要です。
JavaScriptでは、変数を宣言するときに使用するキーワードとして、歴史的経緯からvar
、let
、const
の3種類があります。それぞれ挙動が少しずつ異なり、グローバルスコープへの影響度合いも変わります。
var
はブロックスコープではなく関数スコープを持つ。var
を使うと、自動的にグローバルオブジェクトのプロパティとして登録される。例:トップレベルでのvar
宣言
JavaScript
var name = "Alice";
console.log(window.name); // "Alice" (ブラウザ環境の場合)
このように、ブラウザ環境であればwindow
(グローバルオブジェクト)のプロパティとしてアクセスできます。
let
とconst
はブロック({}
)単位でスコープが作られる。var
との大きな違い。例:トップレベルでのlet
宣言
JavaScript
let greeting = "こんにちは";
console.log(greeting); // "こんにちは"
console.log(window.greeting); // undefined (ブラウザ環境の場合)
greeting
はグローバルスコープに定義されているものの、window
オブジェクトのプロパティにはならず、直接window.greeting
でアクセスすることはできません。
グローバルスコープは強力ですが、多用すると以下のような問題が生じる可能性があります。
複数のスクリプトファイルが同一のグローバル変数名を使用している場合、どちらかが値を上書きしてしまう可能性があります。結果として意図しない動作を招く恐れがあります。
例:ライブラリ同士の衝突
HTML
<script src="libraryA.js"></script>
<script src="libraryB.js"></script>
<script>
// libraryAやlibraryBがそれぞれ "appName" というグローバル変数を定義していた場合、
// 後から読み込んだライブラリによって上書きされる。
</script>
グローバル変数を多用していると、どこかで意図せず値を変更されている可能性が高く、テストを行う際に挙動の影響範囲を把握しづらくなります。
グローバル変数が増えると、コードを読むだけではその変数がどこで定義され、どこから変更を受ける可能性があるか追いかけにくくなります。
昔からよく使われている方法として、即時実行関数を使ってローカルスコープを作り、グローバルスコープを汚染しないようにするやり方があります。モジュールシステムが普及する前には広く利用されていました。
JavaScript
(function() {
var message = "Hello from IIFE";
// message はこの関数内部でのみ有効
console.log(message);
})();
// ここでは message は参照できない
// console.log(message); // Uncaught ReferenceError: message is not defined
グローバルにただの変数を置くのではなく、特定のオブジェクトを1つだけ作り、そこに必要な変数や関数をまとめておくやり方です。これにより、グローバル名前空間の汚染を最小化できます。
JavaScript
// グローバルに MyApp というオブジェクトを定義
var MyApp = MyApp || {};
MyApp.config = {
baseUrl: "/api",
timeout: 5000
};
MyApp.utils = {
formatDate: function(date) {
// ...
}
};
MyApp
オブジェクトはグローバルに一つだけ置き、その中に必要なプロパティをまとめることで、意図しないグローバル変数の上書きを防ぎやすくなります。
現在の推奨は、ES6(ES2015)モジュールを使用してモジュールスコープを作ることです。モジュールを使うことで、ファイルごとにスコープを分離し、グローバルスコープを汚染しない構造を作ることができます。
例:ES6モジュール
moduleA.js
JavaScript
export const message = "Hello from moduleA";
export function greet() {
console.log(message);
}
moduleB.js
JavaScript
import { message, greet } from "./moduleA.js";
console.log(message); // "Hello from moduleA"
greet(); // "Hello from moduleA"
このように、グローバルスコープを使わずに、明示的にimport
/export
でモジュール同士の依存関係を管理できます。ビルド環境や最新のブラウザでは、モジュールが標準的にサポートされています。
window
global
self
これらは各環境固有の名前です。そのため、環境によってコードの書き分けが必要でした。
ECMAScript 2020では、環境に依存しない形でグローバルオブジェクトを参照できるglobalThis
が導入されました。これにより、**「ブラウザかNode.jsかを気にせずに同一の書き方でグローバルオブジェクトを参照できる」**ようになりました。
JavaScript
globalThis.myGlobalValue = 42;
function showGlobalValue() {
console.log(globalThis.myGlobalValue); // 42
}
showGlobalValue();
外部ライブラリや複雑な構造がない、小さなプログラムであれば、シンプルにグローバルスコープを活用しても問題ない場合があります。例えばHTML内に直接JavaScriptを書いて、ページ上の要素にアクセスしたり値を記録したりする程度であれば、大きなリスクはあまりありません。
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>小規模スクリプトの例</title>
</head>
<body>
<h1 id="title">Hello</h1>
<script>
var pageTitle = document.getElementById("title");
pageTitle.style.color = "red";
</script>
</body>
</html>
多くのファイルやライブラリが混在する大規模プロジェクトでは、グローバルスコープの乱用は避けるべきです。
JavaScript
// 例: ES6モジュールと名前空間を併用する
// config.js
export const AppConfig = {
apiUrl: "https://example.com/api",
timeout: 3000
};
// main.js
import { AppConfig } from "./config.js";
(function(global) {
global.MyApp = global.MyApp || {};
MyApp.init = function() {
console.log("API:", AppConfig.apiUrl);
};
})(globalThis);
MyApp.init();
上記のように、モジュールと名前空間を組み合わせることで、明示的に依存関係を管理しながらグローバル名前空間の汚染を最低限に抑えることができます。
"use strict"
(厳格モード)を使用している場合、宣言していない変数に値を代入するとエラーになります。非厳格モードであれば、自動的にグローバル変数が生成されてしまいますが、厳格モードではそれが許可されなくなるため、予期せずグローバルスコープを汚染することを防止できます。
JavaScript
"use strict";
function test() {
// x = 100; // ReferenceError: x is not defined
}
関数やブロックの内部で、グローバルと同名の変数を再宣言すると、グローバル変数が**シャドーイング(隠蔽)**されます。これは意図しないバグの原因になる場合もあるため、可読性を重視した変数名の付け方が大切です。
JavaScript
var foo = "global foo";
function doSomething() {
let foo = "local foo";
console.log(foo); // "local foo"
}
doSomething();
console.log(foo); // "global foo"
ブラウザ環境での特殊な例として、HTML要素のインライン属性で定義したイベントハンドラ(onclick
など)は、自動的にwindow
(グローバルスコープ)に属する関数として参照されます。これもグローバル汚染につながりやすい慣習的な書き方のため、今ではなるべくHTML側に直接書かず、JavaScriptからDOM要素にイベントリスナーを登録する方法が推奨されています。
HTML
<!-- あまり推奨されない例 -->
<button onclick="myGlobalHandler()">Click me</button>
<script>
function myGlobalHandler() {
alert("Hello from global scope!");
}
</script>
var
はトップレベルで宣言するとグローバルオブジェクトに登録されますが、let
・const
はそうではありません。window
、Node.jsではglobal
があり、最新仕様では**globalThis
**が登場して環境依存を解消しています。JavaScriptのグローバルスコープはシンプルな反面、歴史的な理由や仕様上の細かな注意点がたくさん存在します。フロントエンドやバックエンドのいずれでも、グローバルスコープを理解し、適切に管理することが、大規模で安定したアプリケーションの開発・保守に繋がります。