[JavaScript General] JavaScript におけるプロパティとメソッドの宣言

こちらで勉強。
前回の続き。

JavaScript本格入門 ~モダンスタイルによる基礎からAjax・jQueryまで
山田 祥寛
技術評論社
売り上げランキング: 36,098

プロパティはコンストラクタで、メソッドはプロトタイプで宣言する

インスタンス経由で呼び出すインスタンスプロパティとインスタンスメソッド。

  • コンストラクタによるメソッドの追加には、 メソッドの数に比例して無駄なメモリを消費する という大きな問題点がある。
  • JavaScript では、オブジェクトにメンバを追加するために prototype プロパティを利用する。
  • prototype プロパティはディフォルトで空のオブジェクトを参照しており、これにプロパティやメソッドを追加する。
  • オブジェクトをインスタンス化した場合、インスタンスは基となるオブジェクトに属する prototype オブジェクトに対して、暗黙的な参照を持つことになる

前回の「コンストラクタで初期化する」のコードは、ES5 で書くと下記のようになる。

'use strict'
// Property
var Member = function Member(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
};
// Method
Member.prototype.getName = function() {
  return this.lastName + ' ' + this.firstName + '<br />';
}
var mem = new Member('小穂', '田畑');
document.writeln(mem.getName());
var mem2 = new Member('実穂', '田畑');
document.writeln(mem2.getName());

メンバが増えても冗長にならないよう、オブジェクトリテラル表現でこのように書き換えることができる。

// Method
Member.prototype = {
  getName: function() {
    return this.lastName + ' ' + this.firstName + '<br />';
  }
}

このあたりを読むと逆にモヤモヤするけれども、とりあえず置いておく。

補足:ES2015 のオブジェクトリテラル文法の拡張

ちなみにメソッド定義記法により、メソッド部分の書き方は下記のように書き換えられる。
(本来この例では Class を使うので、あくまで記法の例として。)

  getName() {
    return this.lastName + ' ' + this.firstName + '<br />';
  }

アロー関数では this の捕捉がおこってしまう。
このようなケースでは this を束縛しないメソッド記法を利用する。

プロトタイプオブジェクトを利用することの2つの利点

1) メモリの使用量を節減できる

インスタンス化時に、プロトタイプの内容はコピーしない。
インスタンス側に要求されたメンバが無い場合は、暗黙の参照をたどってプロトタイプオブジェクトを検索する。

2) メンバの追加や変更をインスタンスがリアルタイムに認識できる。

上述のコードを入れ替えて、new Member() でインスタンス生成後に Member.prototype.getName でメソッドを追加しても、問題なくメソッドを認識できる。

静的プロパティ・静的メソッドの定義

  • インスタンスを生成しなくてもオブジェクトから直接呼び出せる静的プロパティ・メソッドは、プロトタイプオブジェクトには登録できない。プロトタイプオブジェクトは、インスタンスから暗黙的に参照される事を目的としたオブジェクトのため。
  • コンストラクタ(オブジェクト)に直接追加する。
オブジェクト名.プロパティ名 = 値;
オブジェクト名.メソッド名 = function() {};

静的メンバは、Area という関数オブジェクトに動的に追加されたメンバであり、Area が生成するインスタンスに追加されるわけではない。

'use strict'
// Constructor
var Area = function() {};
// Static Property
Area.version = '1.0';
// Static Method
Area.triangle = function(base, height) {
  return base * height / 2;
}
Area.diamond = function(width, height) {
  return width * height / 2;
}
document.writeln(Area.version + '<br />'); // 1.0
document.writeln(Area.triangle(5, 3) + '<br />'); // 7.5
var a = new Area();
document.writeln(a.diamond(10, 2) + '<br />'); // error

なぜ静的メンバが定義されているのか

  • 静的メンバは、機能的にはグローバル変数・関数と変わらない。
  • グローバル変数・関数は名前が競合する原因となるため、静的メンバを利用することでクラス配下に置き、競合を減らすようにまとめている。
  • 名前空間・パッケージをまとめる際に利用される。空のオブジェクトを利用して擬似的に「名前空間のようなもの」を作成する。

静的プロパティ・静的メソッド利用時の2つの注意点

1) 静的プロパティは、基本的に読み取り専用

「クラス単位で保有される情報」のため、変更がすべてに反映されてしまう。
故に読み取り専用に限定すべき。

2) 静的メソッドの中では、this が使えない

インスタンスメソッド内での this は、インスタンス自身を表す。
静的メソッド内での this は、コンストラクタ(関数オブジェクト)を表す。
故に「静的メソッド内では、this を利用しても意味はない = 使えない」こととなる。

ES2015 のクラス定義に書き換える

static キーワードを使って、クラスに静的メソッドを定義する。
静的プロパティの設定については class 構文に用意されていないそう。

See the Pen JavaScript ES2015 Static property / Static method by DriftwoodJP (@DriftwoodJP) on CodePen.