オブジェクトは通常、ユーザや注文などのように、実世界に存在するものを表現するために作られます:
let user = {
name: "John",
age: 30
};
そして、実世界では、ユーザはショッピングカードから何かを選んだり、ログインしたり、ログアウトしたりと、 アクション することができます。
アクションは、JavaScriptではプロパティの関数として表現されます。
メソッド例
まず初めに、user
が Hello と言うようにしてみましょう:
let user = {
name: "John",
age: 30
};
user.sayHi = function() {
alert("Hello!");
};
user.sayHi(); // Hello!
ここでは関数式を使って関数を作成し、それをオブジェクトのプロパティ user.sayHi
に割り当てています。
この関数は user.sayHi()
という形式で呼び出すことができます。これでユーザが話せるようになりました!
オブジェクトのプロパティとなっている関数は、メソッド と呼ばれます。
つまり、ここでは user
オブジェクトの sayHi
メソッドができたということです。
もちろん、次のように、宣言済みの関数をメソッドとして使うこともできます:
let user = {
// ...
};
// 最初に関数を記述
function sayHi() {
alert("Hello!");
};
// その後、メソッドとして追加
user.sayHi = sayHi;
user.sayHi(); // Hello!
エンティティを表現するためにオブジェクトを使ってコードを書くことを、それは、オブジェクト指向プログラミング(object-oriented programming), 略して “OOP” と呼ばれます。
OOPは大きなものであり、それ自体が興味深い科学です。 どうやって適切なエンティティを選択し、それらの間の相互作用を整理するのか? それがアーキテクチャであり、このトピックについては素晴らしい本があります。 E.Gamma, R.Helm, R.Johnson, J.Vissides による “Design Patterns: Elements of Reusable Object-Oriented Software” や、 G.Booch による “Object-Oriented Analysis and Design with Applications” などです。
メソッドの短縮表現
オブジェクトリテラルでは、メソッドのための短縮構文があります:
// これらのオブジェクトは同じことをします
let user = {
sayHi: function() {
alert("Hello");
}
};
// メソッド簡略化はスッキリ見えますね
let user = {
sayHi() { // "sayHi: function()" と同じです
alert("Hello");
}
};
上の通り、"function"
を省き、単に sayHi()
と書くことができます。
実を言うと、両者の表記は完全に同一ではありません。オブジェクトの継承(後で説明します)に関して微妙な違いがありますが、今のところは問題ありません。ほとんどの場合、短い構文の方が好まれます。
メソッド中の “this”
オブジェクトのメソッドが処理をするために、オブジェクトに格納されている情報にアクセスする必要があることは一般的です。
例えば、user.sayHi()
内のコードが user
の名前を必要とするかもしれません。
オブジェクトにアクセスするために、メソッドは this
キーワードを使うことができます。
this
の値はメソッドを呼び出すのに使われた “ドットの前” のオブジェクトです。
例:
let user = {
name: "John",
age: 30,
sayHi() {
// "this" は "現在のオブジェクト"
alert(this.name);
}
};
user.sayHi(); // John
ここで user.sayHi()
の実行中、this
の値は user
になります。
技術的には、外部変数を介して参照することで、this
を使わずにオブジェクトにアクセスすることも可能です:
let user = {
name: "John",
age: 30,
sayHi() {
alert(user.name); // "this" の代わりに "user"
}
};
…しかし、このようなコードは信頼性に欠けます。もし user
を admin = user
のように別の変数にコピーすることにし、何かでuser
を上書きすると、間違ったオブジェクトにアクセスすることになります。
次のような感じです:
let user = {
name: "John",
age: 30,
sayHi() {
alert( user.name ); // エラーにつながる
}
};
let admin = user;
user = null; // 明らかにするために上書きします
admin.sayHi(); // Whoops! sayHi() の中で古い名前が使われました! エラーです!
alert
の呼び出しで、user.name
ではなく this.name
を使うのであれば、コードは動作するでしょう。
“this” はバインドされていません
JavaScriptでは、 “this” キーワードは他のほとんどのプログラミング言語とは異なる振る舞いをします。オブジェクトのメソッドだけではなく、任意の関数内で使用することができます。
このようなコードも構文エラーにはなりません:
function sayHi() {
alert( this.name );
}
this
の値は、実行時にコンテキストに応じて評価されます。
例えば、ここでは同じ関数が2つの異なるオブジェクトに割り当てられており、呼び出しの際に異なる "this "が使われています:
let user = { name: "John" };
let admin = { name: "Admin" };
function sayHi() {
alert( this.name );
}
// 2つのオブジェクトで同じ関数を使う
user.f = sayHi;
admin.f = sayHi;
// これらの呼び出しは異なる this を持ちます
// 関数の中の "this" は "ドット" の前のオブジェクトです
user.f(); // John (this == user)
admin.f(); // Admin (this == admin)
admin['f'](); // Admin (ドットでも角括弧でも問題なくメソッドにアクセスできます)
ルールはシンプルです。obj.f()
が呼び出されると、f
の呼び出し中は this
は obj
です。つまり、上の例では user
または admin
となります。
this == undefined
オブジェクトがなくても関数を呼び出すことができます:
function sayHi() {
alert(this);
}
sayHi(); // undefined
このケースでは、 strict モードでは this
は undefined
になります。もし this.name
にアクセスしようとするとエラーになります。
非 strict モード(誰かが use strict
を忘れた場合)では、このようなケースでは this
の値は グローバルオブジェクト (ブラウザでは window
、後ほど学びます)になります。これは "use strict"
で修正された歴史的な振る舞いです。
オブジェクトなしで this
を使う関数の呼び出しは、一般的にプログラミングエラーであることに注意してください。関数の中に this
がある場合、それはオブジェクトのコンテキストで呼ばれることを期待しています。
this
の結果もしあなたが他のプログラミング言語から来たのであれば、恐らく “バインドされた this
” の考え方に慣れているでしょう。それは、オブジェクトに定義されたメソッドは常にそのオブジェクトを参照する this
を持っている、と言うものです。
JavaScriptでは、 this
は “自由” です。その値は実行時に評価され、メソッドが宣言されている場所には依存せず、 “ドットの前の” オブジェクトが何であるか、に依存します。
実行時に評価される this
の概念は、プラスとマイナスの両方を持っています。一方では、1つの関数を異なるオブジェクトで再利用することができます。他方では、より大きな柔軟性は、ミスの招きやすさにつながります。
ここで、我々のポジションは、この言語設計上の決定が良いか悪いかを判断するものではありません。我々は、それをどうやって使うか、どうやって利益を得て、どのように問題を回避するかを理解するのです。
アロー関数は “this” を持ちません
アロー関数は特別で、それらは “自身の” this
を持ちません。もしこのような関数で this
を参照した場合、外部の “通常の” 関数から取得されます。
例えば、ここで arrow()
は、外部の user.sayHi()
メソッドから this
を使います:
let user = {
firstName: "Ilya",
sayHi() {
let arrow = () => alert(this.firstName);
arrow();
}
};
user.sayHi(); // Ilya
これはアロー関数の特別な機能です。別の this
ではなく、外側のコンテキストから取り出したい場合に便利です。アロー関数ふたたびのセクションの後半では、より多くのアロー関数を扱います。
サマリ
- オブジェクトのプロパティに格納されている関数は “メソッド” と呼ばれます。
- メソッドは、
object.doSomething()
のように、オブジェクトを “行動” させることができます。 - メソッドはオブジェクトを
this
で参照することができます。
this
の値は実行時に定義されます。
- 関数の宣言時、関数内で
this
を使うことができますが、 そのthis
は関数が呼び出されるまで値を持っていません。 - 関数はオブジェクト間でコピーすることができます。
- 関数が
object.method()
という “メソッド” 構文で呼び出された場合、呼び出し中のthis
の値は、object
です。
アロー関数は特別で、this
を持たないことに注意してください。this
がアロー関数の中でアクセスされるとき、それは外側から取得されます。