2022年10月1日

コンストラクタ、演算子 "new"

通常の {...} 構文では、1つのオブジェクトを作成できます。しかし、多くの場合、複数のユーザーやメニュー項目など、同様のオブジェクトを多数作成する必要があります。

これは、コンストラクタ関数と "new" 演算子を使用して行うことができます。

コンストラクタ関数

コンストラクタ関数は、技術的には通常の関数です。ただし、2つの慣例があります。

  1. 最初の文字は大文字で命名されます。
  2. "new" 演算子を使用してのみ実行する必要があります。

例えば

function User(name) {
  this.name = name;
  this.isAdmin = false;
}

let user = new User("Jack");

alert(user.name); // Jack
alert(user.isAdmin); // false

関数が new で実行されると、次の手順が実行されます。

  1. 新しい空のオブジェクトが作成され、this に割り当てられます。
  2. 関数本体が実行されます。通常は this を変更し、新しいプロパティを追加します。
  3. this の値が返されます。

言い換えれば、new User(...) は次のようなことを行います。

function User(name) {
  // this = {};  (implicitly)

  // add properties to this
  this.name = name;
  this.isAdmin = false;

  // return this;  (implicitly)
}

そのため、let user = new User("Jack") は、次と同じ結果になります。

let user = {
  name: "Jack",
  isAdmin: false
};

これで、他のユーザーを作成したい場合は、new User("Ann")new User("Alice") などを呼び出すことができます。毎回リテラルを使用するよりもはるかに短く、読みやすくなります。

それがコンストラクタの主な目的です。再利用可能なオブジェクト作成コードを実装することです。

もう一度注意してください。技術的には、どの関数(アロー関数を除く。this を持たないため)もコンストラクタとして使用できます。 new で実行でき、上記のアルゴリズムを実行します。「最初の文字は大文字」は、関数が new で実行されることを明確にするための一般的な合意です。

new function() { … }

単一の複雑なオブジェクトの作成に関するコードが多数ある場合は、次のように、すぐに呼び出されるコンストラクタ関数でラップできます。

// create a function and immediately call it with new
let user = new function() {
  this.name = "John";
  this.isAdmin = false;

  // ...other code for user creation
  // maybe complex logic and statements
  // local variables etc
};

このコンストラクタは、どこにも保存されずに作成されて呼び出されるだけなので、再び呼び出すことはできません。そのため、このトリックは、将来再利用することなく、単一のオブジェクトを構築するコードをカプセル化することを目的としています。

コンストラクタモードテスト:new.target

高度な内容

このセクションの構文はめったに使用されません。すべてを知りたい場合を除き、スキップしてください。

関数内では、特別な new.target プロパティを使用して、new を使用して呼び出されたか、使用せずに呼び出されたかを確認できます。

通常の呼び出しでは未定義で、new で呼び出された場合は関数と等しくなります。

function User() {
  alert(new.target);
}

// without "new":
User(); // undefined

// with "new":
new User(); // function User { ... }

これは、関数内で new を使用して「コンストラクタモード」で呼び出されたか、使用せずに「通常モード」で呼び出されたかを知るために使用できます。

次のように、new と通常の呼び出しの両方で同じことを行うこともできます。

function User(name) {
  if (!new.target) { // if you run me without new
    return new User(name); // ...I will add new for you
  }

  this.name = name;
}

let john = User("John"); // redirects call to new User
alert(john.name); // John

このアプローチは、ライブラリで構文をより柔軟にするために使用されることがあります。そのため、人々は new の有無にかかわらず関数を呼び出すことができ、それでも機能します。

ただし、new を省略すると、何が起こっているのかが少しわかりにくくなるため、おそらくどこでも使用するのに適しているわけではありません。 new を使用すると、新しいオブジェクトが作成されていることがわかります。

コンストラクタからの戻り値

通常、コンストラクタには return 文がありません。それらのタスクは、必要なすべてのものを this に書き込むことであり、それが自動的に結果になります。

ただし、return 文がある場合は、ルールは簡単です。

  • return がオブジェクトで呼び出された場合、this の代わりにオブジェクトが返されます。
  • return がプリミティブで呼び出された場合、無視されます。

言い換えれば、オブジェクトを含む return はそのオブジェクトを返し、他のすべての場合では this が返されます。

たとえば、ここでは return はオブジェクトを返すことによって this をオーバーライドします。

function BigUser() {

  this.name = "John";

  return { name: "Godzilla" };  // <-- returns this object
}

alert( new BigUser().name );  // Godzilla, got that object

そして、空の return の例を次に示します(または、その後にプリミティブを配置することもできます。問題ありません)。

function SmallUser() {

  this.name = "John";

  return; // <-- returns this
}

alert( new SmallUser().name );  // John

通常、コンストラクタには return 文がありません。ここでは、主に完全を期すために、オブジェクトを返す特別な動作について説明します。

括弧の省略

ちなみに、new の後の括弧は省略できます。

let user = new User; // <-- no parentheses
// same as
let user = new User();

ここで括弧を省略することは「良いスタイル」とは見なされませんが、構文は仕様で許可されています。

コンストラクタのメソッド

コンストラクタ関数を使用してオブジェクトを作成すると、非常に柔軟になります。コンストラクタ関数には、オブジェクトの作成方法とオブジェクトに配置する内容を定義するパラメータを含めることができます。

もちろん、this にプロパティだけでなく、メソッドも追加できます。

たとえば、以下の new User(name) は、指定された name とメソッド sayHi を持つオブジェクトを作成します。

function User(name) {
  this.name = name;

  this.sayHi = function() {
    alert( "My name is: " + this.name );
  };
}

let john = new User("John");

john.sayHi(); // My name is: John

/*
john = {
   name: "John",
   sayHi: function() { ... }
}
*/

複雑なオブジェクトを作成するには、後で説明するより高度な構文であるクラスがあります。

まとめ

  • コンストラクタ関数、または略してコンストラクタは、通常の関数ですが、最初の文字は大文字で命名するという一般的な合意があります。
  • コンストラクタ関数は、new を使用してのみ呼び出す必要があります。このような呼び出しは、開始時に空の this を作成し、最後に設定されたものを返すことを意味します。

コンストラクタ関数を使用して、複数の同様のオブジェクトを作成できます。

JavaScriptは、日付の Date、セットの Set など、今後学習する予定の多くの組み込み言語オブジェクトのコンストラクタ関数を備えています。

オブジェクト、また後で!

この章では、オブジェクトとコンストラクタの基本についてのみ説明します。次の章でデータ型と関数について詳しく学ぶために不可欠です。

それを学んだ後、オブジェクトに戻り、プロトタイプ、継承クラスの章で詳細に説明します。

タスク

重要度: 2

new A() == new B() となるように関数 AB を作成することは可能ですか?

function A() { ... }
function B() { ... }

let a = new A();
let b = new B();

alert( a == b ); // true

可能な場合は、コードの例を示してください。

はい、可能です。

関数がオブジェクトを返すと、newthis の代わりにそれを返します。

そのため、たとえば、同じ外部定義オブジェクト obj を返すことができます。

let obj = {};

function A() { return obj; }
function B() { return obj; }

alert( new A() == new B() ); // true
重要度: 5

3つのメソッドを持つオブジェクトを作成するコンストラクタ関数 Calculator を作成します。

  • read() は、2つの値のプロンプトを表示し、それぞれ ab という名前のオブジェクトプロパティとして保存します。
  • sum() は、これらのプロパティの合計を返します。
  • mul() は、これらのプロパティの積を返します。

例えば

let calculator = new Calculator();
calculator.read();

alert( "Sum=" + calculator.sum() );
alert( "Mul=" + calculator.mul() );

デモを実行する

テスト付きサンドボックスを開く。

function Calculator() {

  this.read = function() {
    this.a = +prompt('a?', 0);
    this.b = +prompt('b?', 0);
  };

  this.sum = function() {
    return this.a + this.b;
  };

  this.mul = function() {
    return this.a * this.b;
  };
}

let calculator = new Calculator();
calculator.read();

alert( "Sum=" + calculator.sum() );
alert( "Mul=" + calculator.mul() );

サンドボックスでテスト付きのソリューションを開く。

重要度: 5

コンストラクタ関数 Accumulator(startingValue) を作成します。

それが作成するオブジェクトは、

  • プロパティ value に「現在の値」を格納する必要があります。開始値はコンストラクタの引数 startingValue に設定されます。
  • read() メソッドは、prompt を使用して新しい数値を読み取り、value に追加する必要があります。

言い換えれば、value プロパティは、ユーザーが入力したすべての値と初期値 startingValue の合計です。

コードのデモは次のとおりです。

let accumulator = new Accumulator(1); // initial value 1

accumulator.read(); // adds the user-entered value
accumulator.read(); // adds the user-entered value

alert(accumulator.value); // shows the sum of these values

デモを実行する

テスト付きサンドボックスを開く。

function Accumulator(startingValue) {
  this.value = startingValue;

  this.read = function() {
    this.value += +prompt('How much to add?', 0);
  };

}

let accumulator = new Accumulator(1);
accumulator.read();
accumulator.read();
alert(accumulator.value);

サンドボックスでテスト付きのソリューションを開く。

チュートリアルマップ

コメント

コメントする前にこれを読んでください…
  • 改善すべき点がある場合は、コメントする代わりにGitHubのissueを提出するか、プルリクエストを送信してください。
  • 記事の内容が理解できない場合は、詳しく説明してください。
  • 数語のコードを挿入するには、<code> タグを使用し、複数行の場合は <pre> タグで囲み、10行を超える場合はサンドボックス(plnkrjsbincodepen…)を使用してください。