getter / setter とは何か(まずイメージから)
getter / setter は、
「プロパティに見える“入り口・出口”の裏に、処理を仕込むための仕組み」 です。
普通のプロパティはこうです。
user.age = 20;
console.log(user.age);
JavaScriptgetter / setter を使うと、見た目は同じ書き方をしながら、
実際には「関数」が呼ばれます。
class User {
#age = 0;
get age() { // 読み取り時に呼ばれる
console.log("getter が呼ばれた");
return this.#age;
}
set age(value) { // 書き込み時に呼ばれる
console.log("setter が呼ばれた:", value);
if (value < 0) {
throw new Error("年齢は0以上にしてください");
}
this.#age = value;
}
}
const u = new User();
u.age = 20; // setter が呼ばれる
console.log(u.age); // getter が呼ばれる → 20
JavaScriptここが重要です。u.age と書いていても、実際には
読むときは get age() が、書くときは set age(...) が呼ばれています。
「プロパティのように見える関数」 とイメージすると理解しやすいです。
クラスでの getter / setter の基本構文
getter の書き方(読み取り専用の“プロパティ”)
get プロパティ名() { ... } で定義します。
class Rectangle {
constructor(width, height) {
this.width = width;
this.height = height;
}
get area() { // 面積を“プロパティのように”取得できる
return this.width * this.height;
}
}
const r = new Rectangle(10, 5);
console.log(r.area); // 50(r.area() ではないことに注目)
JavaScriptarea はメソッドではなく、r.area と書いた瞬間に get area() が呼ばれ、その戻り値が返ってきます。
ここが重要です。
getter を使うと、「計算が必要な値」を「普通のプロパティのような顔」で提供できます。
読み取り専用にしたいときも便利です(setter を定義しなければ書き込めない)。
setter の書き方(代入時のチェックや変換)
set プロパティ名(value) { ... } で定義します。
class User {
#name = "";
get name() {
return this.#name;
}
set name(value) {
if (value.length === 0) {
throw new Error("名前は空にできません");
}
this.#name = value;
}
}
const u = new User();
u.name = "Alice"; // setter が呼ばれてチェックされる
console.log(u.name); // getter 経由で表示 → Alice
// u.name = ""; // エラー:名前は空にできません
JavaScript代入側は普通に u.name = "Alice" と書くだけなのに、
裏ではバリデーション(チェック)や変換処理を走らせられます。
getter と setter をペアで定義する
同じ名前で get と set の両方を定義すると、「読み書き可能なプロパティ」を作れます。
class Counter {
#value = 0;
get value() {
return this.#value;
}
set value(v) {
if (typeof v !== "number") {
throw new Error("数値を指定してください");
}
this.#value = v;
}
}
const c = new Counter();
c.value = 10; // set value(10)
console.log(c.value); // get value() → 10
JavaScriptgetter / setter をなぜ使うのか(重要な設計の話)
「直接触らせたくないけど、値は見せたい・変えさせたい」
プライベートフィールド(#)と組み合わせると、
中身は隠したまま、安全な窓口だけを用意できます。
class BankAccount {
#balance = 0;
get balance() {
return this.#balance;
}
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;
}
}
const acc = new BankAccount();
acc.deposit(1000);
console.log(acc.balance); // getter 経由で残高を見る
// acc.balance = 9999; // setter がないので書き換え不可(TypeError になる環境もある)
JavaScriptここが重要です。
内部の生データ(#balance)を直接いじらせず、
「見せ方」「変更のルール」を getter / メソッドに閉じ込める。
これがカプセル化(中身の保護)の具体的な形です。
「内部の表現」と「外から見える形」を分離できる
例えば、温度を内部ではケルビンで持ち、外からは摂氏で扱いたい場合:
class Temperature {
#kelvin = 0;
constructor(celsius) {
this.celsius = celsius; // setter を経由
}
get celsius() {
return this.#kelvin - 273.15;
}
set celsius(value) {
this.#kelvin = value + 273.15;
}
get kelvin() {
return this.#kelvin;
}
}
const t = new Temperature(25); // 25℃
// 内部的には #kelvin に変換されて保存される
console.log(t.celsius); // 25
console.log(t.kelvin); // 298.15 くらい
JavaScript使う側は「摂氏で扱いたい」のに、
中では「ケルビン」、と内部表現を自由に変えられます。
後から内部構造を変えても、celsius プロパティのインターフェースは維持できます。
getter / setter と普通のメソッドの違い
見た目が「プロパティアクセス」か「関数呼び出し」か
普通のメソッドならこうです。
user.getAge();
user.setAge(20);
JavaScriptgetter / setter だとこう書けます。
user.age; // getter
user.age = 20; // setter
JavaScriptどちらでも機能的には可能ですが、
「値っぽいもの」はプロパティ(+getter/setter)として扱うほうが自然です。
// よくない例
user.getName();
user.setName("Bob");
// こちらの方が自然
user.name;
user.name = "Bob";
JavaScriptここが重要です。
「物(状態)」を表現したいなら getter / setter、「動き(動作)」ならメソッド
という切り分けを意識すると、API の読みやすさがかなり変わります。
内部実装を後から変えられる
最初は単純な公開プロパティにしておき、
後から getter / setter に変えても、呼び出し側のコードは変えずに済みます。
// 最初:単純なプロパティ
class User {
constructor(name) {
this.name = name;
}
}
// いずれ:チェックを入れたい → getter / setter に変える
class User {
#name = "";
constructor(name) {
this.name = name; // setter 経由
}
get name() {
return this.#name;
}
set name(value) {
if (!value) throw new Error("名前が空です");
this.#name = value;
}
}
JavaScript使う側はずっと user.name / user.name = ... と書くだけでよく、
裏側の実装を差し替えても影響が少なく済みます。
クラス継承と getter / setter
親クラスの getter / setter をそのまま使う
継承すると、getter / setter も普通のメソッドと同じように引き継がれます。
class Person {
#name = "";
get name() {
return this.#name;
}
set name(value) {
this.#name = value;
}
}
class Employee extends Person {
// name の getter / setter をそのまま使える
}
const e = new Employee();
e.name = "Alice";
console.log(e.name); // Alice
JavaScriptオーバーライドして挙動を変える
子クラスで、同じ名前の getter / setter を定義し直すこともできます。
class Person {
#name = "";
get name() {
return this.#name;
}
set name(value) {
this.#name = value;
}
}
class Employee extends Person {
get name() {
return "[社員]" + super.name;
}
set name(value) {
super.name = value.trim(); // 前後の空白を削ってからセット
}
}
const e = new Employee();
e.name = " Bob ";
console.log(e.name); // [社員]Bob
JavaScriptsuper.name は、親クラスの getter / setter を通じたアクセスになり、
「親のルール+子の追加仕様」をきれいに組み合わせられます。
例題で getter / setter の感覚を固める
例1:フルネームを計算する getter / setter
class User {
constructor(first, last) {
this.firstName = first;
this.lastName = last;
}
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
set fullName(value) {
const [first, last] = value.split(" ");
this.firstName = first;
this.lastName = last;
}
}
const u = new User("Alice", "Smith");
console.log(u.fullName); // Alice Smith
u.fullName = "Bob Brown";
console.log(u.firstName); // Bob
console.log(u.lastName); // Brown
JavaScript例2:読み取り専用プロパティ
class Order {
constructor(price, quantity) {
this.price = price;
this.quantity = quantity;
}
get total() {
return this.price * this.quantity;
}
}
const o = new Order(500, 3);
console.log(o.total); // 1500
// o.total = 9999; // setter がないので書き込めない
JavaScript例3:内部的な単位と外部インターフェースの分離
class Distance {
#meters = 0;
constructor(meters) {
this.#meters = meters;
}
get meters() {
return this.#meters;
}
set meters(value) {
this.#meters = value;
}
get km() {
return this.#meters / 1000;
}
set km(value) {
this.#meters = value * 1000;
}
}
const d = new Distance(1500); // 1500m
console.log(d.km); // 1.5
d.km = 2; // 2km に変更
console.log(d.meters); // 2000
JavaScriptまとめ
getter / setter の核心は、
「プロパティアクセス(obj.prop / obj.prop =)の裏側で、任意の処理を挟めるようにする」 ことです。
押さえておきたいポイントは次の通りです。
get name()/set name(value)の形でクラスに定義する- 読むときは
obj.name、書くときはobj.name = ...と普通のプロパティのように扱える - 中ではプライベートフィールドと組み合わせて、チェック・変換・計算・隠蔽ができる
- 「状態」を表現したい場合は getter / setter、「動作」は普通のメソッドという切り分けが有効
- 後から内部実装を変えても、プロパティとしてのインターフェースを保ちやすい
まずは、自分のクラスの中で「実は読み取り専用にしたかった値」や
「セット時にチェックしたい値」を1つ選んで、getter / setter に置き換えてみてください。
そこから、「状態の出入り口を設計する」という感覚が掴めてきます。
