new 演算子は、コンストラクタ関数や class から新しいオブジェクト(インスタンス)を生成するための演算子です。オブジェクト指向プログラミングの基本になります。
この記事では、new が内部で何をしているかを押さえたうえで、現代的な class 構文や、メソッドを共有する仕組み、よくある落とし穴まで解説します。
new コンストラクタ() で新しいオブジェクトを生成します。内部では「空オブジェクト生成 → prototypeリンク → this束縛 → 返却」が行われます。現代のJavaScriptではclass 構文が標準で、new を忘れても TypeError で気づける安全さがあります。newの基本(コンストラクタ関数)
まずは古くからあるコンストラクタ関数の例です。関数を「オブジェクトのひな型」として使い、new を付けて呼び出します。
function Person(name, age) {
this.name = name;
this.age = age;
}
const person1 = new Person("Alice", 25);
console.log(person1.name); // "Alice"
console.log(person1.age); // 25
newが内部でやっていること(4ステップ)
new Person("Alice", 25) を実行すると、次の順で処理されます。
- 新しい空のオブジェクトが作られる
- そのオブジェクトの内部プロトタイプが、コンストラクタの
prototypeを指すようになる - コンストラクタが
thisを新しいオブジェクトに束縛して実行される - コンストラクタが別のオブジェクトを明示的に返さない限り、新しいオブジェクトが返される
2番目のプロトタイプのつながりは、Object.getPrototypeOf() で確認できます(非推奨の __proto__ ではなくこちらを使います)。
const p = new Person("Bob", 30);
console.log(Object.getPrototypeOf(p) === Person.prototype); // true
メソッドはprototypeで共有する
全インスタンスで共通の処理は、prototype にメソッドを定義します。するとそのメソッドは全インスタンスで共有され、メモリ効率が良くなります。
function Person(name) {
this.name = name;
}
Person.prototype.greet = function () {
return `こんにちは、${this.name}です`;
};
const a = new Person("Alice");
const b = new Person("Bob");
console.log(a.greet()); // "こんにちは、Aliceです"
// メソッドの実体は1つだけ(全インスタンスで共有)
console.log(a.greet === b.greet); // true
現代的な書き方:class
ES2015以降は class 構文が標準です。constructor で初期化し、メソッドをそのまま書けば prototype に定義されます。見た目が分かりやすく、継承も extends で簡潔に書けます。
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
return `こんにちは、${this.name}です`;
}
}
const p = new Person("Alice", 25);
console.log(p.greet());
class Student extends Person {
constructor(name, age, school) {
super(name, age); // 親のコンストラクタを呼ぶ
this.school = school;
}
greet() {
return `${super.greet()}(${this.school})`;
}
}
const s = new Student("Bob", 20, "A大学");
console.log(s.greet()); // "こんにちは、Bobです(A大学)"
型を付けてより安全に書きたい場合は、TypeScriptのクラスの型定義 完全ガイドも参考になります(アクセス修飾子や抽象クラスまで解説)。
instanceof と new.target
あるオブジェクトが特定のクラス(コンストラクタ)から作られたかは instanceof で判定します。new.target を使うと、関数が new 付きで呼ばれたかを検知できます。
console.log(p instanceof Person); // true
console.log(s instanceof Person); // true(継承元もtrue)
// new を忘れたら例外にする
function Foo() {
if (!new.target) {
throw new Error("new を付けて呼んでください");
}
}
newを使わずにオブジェクトを作る
1つだけのオブジェクトや、継承が不要な場合は new を使わない方法も便利です。
// ① オブジェクトリテラル(最も手軽)
const obj = { name: "Alice", greet() { return `Hi ${this.name}`; } };
// ② Object.create(プロトタイプを指定して作る)
const proto = { greet() { return "hi"; } };
const o = Object.create(proto);
// ③ ファクトリ関数(newなしでオブジェクトを返す)
function createPerson(name) {
return { name, greet() { return `Hi ${this.name}`; } };
}
const person = createPerson("Alice");
生成したオブジェクトを配列にまとめて処理する場面では、map・filter・reduceの使い方や配列から条件に合う要素を取り出す方法が役立ちます。
newを忘れたときの落とし穴
new なしでコンストラクタ関数を呼ぶと、this が新オブジェクトに束縛されません。strictモードでは this が undefined になりエラー、非strictでは globalThis を汚染してしまいます。function Person(name) {
this.name = name;
}
// NG: new を忘れた
const p = Person("Alice"); // strictモードでは TypeError(this が undefined)
// class なら new 忘れは即 TypeError で気づける(安全)
class Animal {}
Animal(); // TypeError: Class constructor Animal cannot be invoked without 'new'
この点でも、new 忘れを TypeError で防げる class の方が安全です。
よくある質問(FAQ)
class をインスタンス化します。「空オブジェクト作成 → prototype リンク → コンストラクタ実行 → インスタンス返却」の順で処理されます。{}、Object.create()、ファクトリ関数(通常の関数でオブジェクトを返す)が使えます。class より軽量で、カプセル化しやすいファクトリパターンも有用です。this がグローバルオブジェクトを指します(strictモードでは undefined になりエラー)。class は new 無しで呼ぶと TypeError になるため、より安全です。new でインスタンスを作りますが、class はメソッド定義や継承(extends / super)が分かりやすく、new 忘れを防げます。現代のJavaScriptでは class が標準です。まとめ
new 演算子は、コンストラクタや class から新しいオブジェクトを生成します。内部では「空オブジェクト → prototype リンク → this 束縛 → 返却」が行われ、共通メソッドは prototype に置くことで全インスタンスで共有されます。
現代のJavaScriptでは class 構文が標準で、継承も簡潔に書け、new 忘れを TypeError で防げます。1つだけのオブジェクトや継承が不要な場合は、リテラルやファクトリ関数も検討しましょう。
