こんにちは!日々の開発、本当にお疲れ様です。
JavaScriptの変数やスコープの仕組み、そしてオブジェクト指向のコードを書くうえで避けて通れない「カプセル化」について、今日は少し踏み込んでお話ししていきますね。
他のプログラミング言語(JavaやC++、Pythonなど)を触ったことがある方なら、「クラスの中に外部から触られたくない隠し変数(プライベート変数)を持たせたい」と思った経験が一度はあるはずです。JavaScriptでも長年、この「隠しデータ」の扱いは悩ましい問題でした。
しかし、現代のモダンなJavaScriptには、言語仕様として完璧にプライベートを守る「プライベートフィールド(`#`)」が標準装備されています。ここをしっかりとクリアすれば、あなたの書くコードの安全性と美しさは一気にプロフェッショナルな領域に到達しますよ。
今回は、従来のスコープやクロージャの限界を踏まえながら、新しいカプセル化の標準であるプライベートフィールドの本質を一緒に紐解いていきましょう!
—
1. そもそも、なぜ「隠す(カプセル化する)」必要があるの?
プログラムの規模が大きくなると、「外部から絶対に書き換えられてはいけないデータ」や「オブジェクトの内部状態を示す重要な数値」が出てきます。
例えば、銀行口座の残高を管理するプログラムを想像してみてください。もし、残高データが外部から自由にいじれたらどうなるでしょうか?
`account.balance = 99999999;` のように、誰でも勝手に大金に書き換えられては大問題ですよね。
このように、「オブジェクトの内部構造や重要なデータを隠し、安全な決まった手順(メソッド)を通してのみ操作させること」をカプセル化と呼びます。
—
2. 従来のJavaScriptにおける「隠蔽」の歴史と限界
プライベートフィールドが登場する前、JavaScriptのエンジニアたちは知恵を絞って「隠し変数」を作ってきました。その歴史を軽く振り返っておきましょう。
① 慣習による表現(アンダースコア `_`)
一番原始的な方法は、変数名の頭にアンダースコアをつけることです。
class BankAccount {
constructor(balance) {
this._balance = balance; // 「触らないでね」という開発者間の『お願い』
}
}
const myAccount = new BankAccount(1000);
console.log(myAccount._balance); // 1000 (でも、普通に外から読めてしまうし書き換えられる)
これは単なる「マナー」にすぎません。JavaScriptのランタイム(V8エンジンなど)はこれをプライベートとして認識していないため、外からいくらでもアクセスできてしまいます。これでは真のカプセル化とは言えませんよね。
② クロージャによる隠蔽(コンストラクタ内での変数定義)
次に、関数のスコープ(クロージャ)の性質を利用してデータを完全に隠す方法が流行しました。
function createAccount(initialBalance) {
let balance = initialBalance; // 外からは絶対に触れない変数
return {
getBalance() {
return balance;
},
deposit(amount) {
if (amount > 0) balance += amount;
}
};
}
const myAccount = createAccount(1000);
console.log(myAccount.balance); // undefined (隠すことに成功!)
確かにこの方法はデータを完全に隠せますが、「クラス構文 (`class`) が使えない」「インスタンスを作るたびにメソッドがメモリ上に複製されてしまい、メモリ効率が悪い」というデメリットがありました。
—
3. 救世主登場!プライベートフィールド(`#`)の基本
そこでJavaScript(ES2022で正式標準化)に導入されたのが、ハッシュ記号(`#`)をプレフィックスにつけるプライベートフィールドです。
百聞は一見にしかず。実際のコードを見てみましょう。
class BankAccount {
// プライベートフィールドの宣言(必ずクラスの最上位で宣言する必要があります)
#balance;
constructor(initialBalance) {
this.#balance = initialBalance; // クラス内からは # でアクセス可能
}
// 残高を確認するメソッド(getter)
getBalance() {
return this.#balance;
}
// 入金するメソッド
deposit(amount) {
if (amount > 0) {
this.#balance += amount;
}
return this.#balance;
}
}
const myAccount = new BankAccount(1000);
console.log(myAccount.getBalance()); // 1000 を出力
// console.log(myAccount.#balance); // ❌ SyntaxError!外からは絶対にアクセスできません!
💡 ここがポイント:シンタックスレベルでの完全なガード
変数の頭に `#` をつけるだけで、V8などのJavaScriptエンジンは「このプロパティはクラスの外から絶対に触らせない」と厳格に監視します。
もし外から `#balance` にアクセスしようものなら、実行時エラーではなく、コードをパース(解析)する段階で `SyntaxError`(構文エラー)を吐いて止めてくれます。未然にバグを防げるのは最高に心強いですよね。
—
4. 陥りやすい文法エラーと注意すべきポイント
プライベートフィールドは非常に強力ですが、初学者が思わずハマりがちな「落とし穴」がいくつかあります。ここをマスターすればバッチリです!
落とし穴1: 事前宣言なしに使おうとする
従来のJavaScript(クラスプロパティの提案前)の感覚で、コンストラクタ内でいきなり `#` 付きの変数を代入しようとするとエラーになります。
class User {
constructor(name) {
// this.#name = name;
// ❌ エラー! #name が事前にクラスのトップレベルで宣言されていません
}
}
正しい書き方:
class User {
#name; // 先に宣言する(初期値を同時に代入することも可能です:#name = ‘名無し’;)
constructor(name) {
this.#name = name; // OK
}
}
落とし穴2: クラスの「外」からは、インスタンス経由でも一切見えない
「自分の作ったオブジェクトのインスタンスなんだから、中身を見せてよ!」と思うかもしれませんが、JavaScriptのプライベートはクラス単位です。
class Box {
#secret = ‘ひみつの宝物’;
showSecret(otherBox) {
// 同じクラスの別のインスタンス(otherBox)のプライベート変数ならアクセスできる!
console.log(otherBox.#secret);
}
}
const box1 = new Box();
const box2 = new Box();
box1.showSecret(box2); // これなら動く(同一クラス内だから)
// でも、クラスの外からはダメ
// console.log(box1.#secret); ❌ 構文エラー
このように、「同じクラスの設計図から生まれた仲間同士ならプライベートを共有できるが、完全にクラスの外からは遮断される」というスコープの新しい概念がここで生まれています。
—
5. プライベートメソッドや静的(static)プライベートもある!
実は、データ(フィールド)だけでなく、メソッド(関数)自体もプライベートにすることができます。さらに、インスタンス化せずに使う `static` な変数やメソッドもプライベートにすることが可能です。
class AuthenticationService {
// プライベートな静的フィールド
static #API_KEY = ‘secret_abc123’;
// プライベートなメソッド(外部からは呼び出せないヘルパー関数)
#validateToken(token) {
return token === AuthenticationService.#API_KEY;
}
login(token) {
if (this.#validateToken(token)) {
console.log(‘ログイン成功!’);
} else {
console.log(‘認証エラー!’);
}
}
}
const auth = new AuthenticationService();
auth.login(‘secret_abc123’); // 「ログイン成功!」
// auth.#validateToken(‘abc’); ❌ 外からは呼び出せない!
複雑な内部処理のロジック(外部に公開する必要のない関数)を `#` で隠すことで、クラスのインターフェース(外部に公開するメソッド)を非常にシンプルに保つことができます。これがモダンな設計の醍醐味です。
—
おわりに
いかがでしたでしょうか?
JavaScriptのプライベートフィールド(`#`)は、単なる「お化粧」ではなく、V8などのランタイムレベルでガチガチに保護された、現代の堅牢なアプリケーション開発に欠かせない強力な武器です。
これらを使いこなせるようになると、コードの保守性が劇的に向上し、大規模なアプリを作るときにも「意図しないバグ」に怯えることがなくなります。
「ここをクリアすれば、JavaScriptの基本はバッチリマスターできますよ!」
ぜひ今日のコードをご自身の環境でも動かして、その挙動を体感してみてくださいね。あなたのエンジニアリングライフを心から応援しています!