プライベートフィールド(#)とは何か
プライベートフィールドは、class の中で
「クラスの外から絶対に直接触られたくない値」を隠すための仕組みです。
書き方は、名前の前に # をつけます。
class User {
#name; // プライベートフィールド
constructor(name) {
this.#name = name;
}
greet() {
console.log(`こんにちは、${this.#name} です`);
}
}
const u = new User("Alice");
u.greet(); // こんにちは、Alice です
console.log(u.#name); // エラー:クラスの外からはアクセスできない
JavaScriptここが重要です。#name は クラスの外からも、継承した子クラスからも、インスタンス経由でも「直接」は触れない 完全プライベートなフィールドです。
「class の中だけの秘密」と思ってください。
どう書くのか(基本構文とルール)
宣言と代入の基本パターン
プライベートフィールドを使うときは、クラスの一番上あたりで宣言します。
class Counter {
#value; // 宣言
constructor(initial = 0) {
this.#value = initial; // 代入
}
increment() {
this.#value++;
}
getValue() {
return this.#value;
}
}
const c = new Counter(10);
c.increment();
console.log(c.getValue()); // 11
JavaScriptポイントは次の2つです。
- クラスの中で
#名前を宣言する - そのクラスの中でだけ
this.#名前で読み書きできる
クラスの外で c.#value と書くと、即エラーになります。
プロパティ名は「#付き」と「#なし」で別物
#name と name は、完全に別のプロパティです。
class User {
#name;
constructor(name) {
this.#name = name;
this.name = name + "(公開用)";
}
show() {
console.log("private:", this.#name);
console.log("public :", this.name);
}
}
const u = new User("Alice");
u.show();
// private: Alice
// public : Alice(公開用)
console.log(u.name); // OK(公開プロパティ)
console.log(u.#name); // エラー(プライベート)
JavaScriptここが重要です。#name と name は、全く別のフィールド。# が付くと「完全なプライベート」、付かないものは普通のプロパティ(外から見える)です。
なぜプライベートフィールドが必要なのか(カプセル化の本質)
「触られたら困る中身」を守る
例えば銀行口座クラスを考えます。
class BankAccount {
#balance; // 外から直接いじってほしくない
constructor(initialBalance = 0) {
this.#balance = initialBalance;
}
deposit(amount) {
if (amount <= 0) throw new Error("金額がおかしい");
this.#balance += amount;
}
withdraw(amount) {
if (amount <= 0) throw new Error("金額がおかしい");
if (this.#balance < amount) throw new Error("残高不足");
this.#balance -= amount;
}
getBalance() {
return this.#balance;
}
}
const account = new BankAccount(1000);
account.deposit(500);
// account.#balance = 999999; // こういうズルを完全に防げる
console.log(account.getBalance()); // 1500
JavaScriptもし # をつけないと、外からこう書けてしまいます。
account.balance = 999999; // 本当はやられたくない
ここが重要です。
プライベートフィールドは、「外から勝手にいじられると困る内部状態」を守る壁です。
クラスの外からは、ちゃんと用意されたメソッド(deposit, withdraw など)を通してしか変更できなくなります。
「外から見せたい情報」と「隠したい内部」を分ける
設計としては、よくこうします。
- プライベートフィールド:内部の生データ(
#balance,#passwordなど) - パブリックメソッド:そのデータを安全に扱うための機能(
deposit,checkPasswordなど)
class User {
#passwordHash;
constructor(name, password) {
this.name = name;
this.#passwordHash = this.#hash(password);
}
#hash(text) { // プライベートメソッド
return `hashed:${text}`;
}
checkPassword(password) {
return this.#passwordHash === this.#hash(password);
}
}
const u = new User("Alice", "secret");
console.log(u.name); // 公開OK
console.log(u.checkPassword("xxx")); // false
console.log(u.checkPassword("secret"));// true
// console.log(u.#passwordHash); // エラー
// u.#hash("secret"); // エラー
JavaScriptここが重要です。
クラスの中だけで完結させたい“裏側のロジック”は全部 # 付きにする、
これがカプセル化(中身の隠蔽)の実践です。
プライベートメソッドも書ける
#付きでメソッドも隠せる
フィールドだけでなく、メソッドにも # をつけられます。
class Logger {
#prefix;
constructor(prefix = "LOG") {
this.#prefix = prefix;
}
#format(message) { // プライベートメソッド
const time = new Date().toISOString();
return `[${this.#prefix}] ${time} - ${message}`;
}
log(message) { // 公開メソッド
console.log(this.#format(message));
}
}
const logger = new Logger("APP");
logger.log("起動しました");
// logger.#format("直接呼びたい"); // エラー:外から呼べない
JavaScriptここが重要です。
「外から直接呼ばせたくない補助的な処理」は、#付きメソッドにしてしまう と、
クラスの「公開API」と「内部実装」が綺麗に分かれます。
継承(extends)とプライベートフィールド
子クラスからも直接触れない
プライベートフィールドは「そのクラス専用」です。
継承した子クラスからも直接は触れません。
class Animal {
#name;
constructor(name) {
this.#name = name;
}
getName() {
return this.#name;
}
}
class Dog extends Animal {
bark() {
// console.log(this.#name); // エラー:親の #name は見えない
console.log(this.getName() + "「ワン!」");
}
}
const d = new Dog("ポチ");
d.bark(); // ポチ「ワン!」
JavaScriptここが重要です。
プライベートフィールドは “クラス単位” の秘密であって、“継承階層全体” の秘密ではない。
子クラスに見せたい場合は、親クラス側で protected 的な役割のメソッド(getName() など)を用意してあげます。
旧来の「擬似プライベート」との違い
「慣習」ではなく「言語仕様としての隠蔽」
昔は、「プライベートっぽく見せる」ためにこんな書き方をすることがありました。
class OldStyle {
constructor() {
this._value = 0; // 先頭に _ をつけて「触らないでね」という合図
}
}
JavaScriptしかしこれは「約束」にすぎず、普通に外から触れてしまいます。
const o = new OldStyle();
o._value = 999; // 触れてしまう
JavaScript#value は、「仕様として絶対に触れない」ので、
「約束」ではなく「本物のプライベート」 になっています。
初心者向けにざっくり言うと:
_value→ 「本当は触らないでほしいけど、技術的には触れてしまう」#value→ 「そもそも構文エラーになるので触れない」
という違いです。
例題でプライベートフィールドに慣れる
例1:安全なカウンター
class Counter {
#value;
constructor(initial = 0) {
this.#value = initial;
}
increment() {
this.#value++;
}
decrement() {
this.#value--;
}
get value() { // getter で外に出す
return this.#value;
}
}
const c = new Counter(10);
c.increment();
c.decrement();
console.log(c.value); // 10
// c.#value = 999; // エラー:外から改ざんできない
JavaScript例2:パスワード付きユーザー
class SecureUser {
#passwordHash;
constructor(name, password) {
this.name = name;
this.#passwordHash = this.#hash(password);
}
#hash(text) {
// 実際はもっと複雑なハッシュ関数を使う
return `hash:${text}`;
}
checkPassword(password) {
return this.#passwordHash === this.#hash(password);
}
}
const u = new SecureUser("Alice", "secret");
console.log(u.checkPassword("secret")); // true
console.log(u.checkPassword("xxx")); // false
// console.log(u.#passwordHash); // エラー
JavaScript例3:内部状態を隠した設定クラス
class Config {
#options;
constructor(options = {}) {
this.#options = {
theme: "light",
lang: "ja",
...options
};
}
get(key) {
return this.#options[key];
}
set(key, value) {
this.#options[key] = value;
}
dump() {
return { ...this.#options }; // コピーを返す
}
}
const cfg = new Config({ theme: "dark" });
console.log(cfg.get("theme")); // dark
cfg.set("lang", "en");
console.log(cfg.dump()); // { theme: 'dark', lang: 'en' }
// cfg.#options = {}; // エラー:内部構造は隠蔽
JavaScriptまとめ
プライベートフィールド(#)の核心は、
「クラスの中だけで使える、本物のプライベートなプロパティやメソッドを持てるようにする」 ことです。
押さえておきたいポイントは:
#nameのように宣言し、クラスの中でthis.#nameとして使う- クラスの外からも、子クラスからも、
obj.#nameは構文エラーになる - 「外から触られると困るデータ・ロジック」を
#付きにして、メソッド経由でだけ操作させる - プライベートメソッド(
#method() {})も同じように定義できる - 旧来の
_nameのような「慣習」ではなく、仕様として守られる本物のカプセル化
まずは、自分で作ったクラスの中で「絶対に外から触られてほしくない値」を1つ決めて、
それを # 付きフィールドにしてみてください。
クラスの「外に見せる顔」と「内側」の境界線が、ぐっとはっきりして見えてくるはずです。
