オブジェクトは通常、ユーザーや注文などの現実世界のエンティティを表すために作成されます。
ユーザー = { にします 名前:「ジョン」、 年齢: 30歳 };
そして、現実の世界では、ユーザーはショッピング カートから何かを選択したり、ログインしたり、ログアウトしたりするなどの操作を行うことができます。
アクションは、JavaScript ではプロパティ内の関数によって表されます。
まず、 user
挨拶するように教えてみましょう。
ユーザー = { にします 名前:「ジョン」、 年齢: 30歳 }; user.sayHi = function() { アラート("こんにちは!"); }; user.sayHi(); // こんにちは!
ここでは、関数式を使用して関数を作成し、それをオブジェクトのプロパティuser.sayHi
に割り当てました。
次に、それをuser.sayHi()
として呼び出すことができます。ユーザーは話すことができるようになりました。
オブジェクトのプロパティである関数は、そのオブジェクトのメソッドと呼ばれます。
したがって、ここにはオブジェクトuser
のメソッドsayHi
あります。
もちろん、次のように、事前に宣言された関数をメソッドとして使用することもできます。
ユーザー = { にします // ... }; // まず宣言します 関数sayHi() { アラート("こんにちは!"); } // 次にメソッドとして追加します user.sayHi = 言ってください; user.sayHi(); // こんにちは!
オブジェクト指向プログラミング
オブジェクトを使用してエンティティを表すコードを記述することを、オブジェクト指向プログラミング、つまり「OOP」と呼びます。
OOP は重要なものであり、それ自体が興味深い科学です。適切なエンティティを選択するにはどうすればよいですか?それらの間の相互作用をどのように組織するか?それがアーキテクチャであり、E. ガンマ、R. ヘルム、R. ジョンソン、J. ヴィサイドによる『デザイン パターン: 再利用可能なオブジェクト指向ソフトウェアの要素』や、『オブジェクト指向分析と設計』など、このトピックに関する優れた書籍があります。 Applications』(G. Booch著)など。
オブジェクト リテラルのメソッドには、より短い構文が存在します。
// これらのオブジェクトは同じことを行います ユーザー = { SayHi: function() { アラート("こんにちは"); } }; // メソッドの短縮表現のほうが分かりやすいですよね? ユーザー = { SayHi() { // 「sayHi:function(){...}」と同じ アラート("こんにちは"); } };
示されているように、 "function"
省略して、単にsayHi()
と書くことができます。
実を言うと、表記は完全に同一ではありません。オブジェクトの継承 (後で説明します) に関連する微妙な違いがありますが、今のところは問題ではありません。ほとんどの場合、短い構文が優先されます。
オブジェクト メソッドがそのジョブを実行するために、オブジェクトに格納されている情報にアクセスする必要があるのは一般的です。
たとえば、 user.sayHi()
内のコードにはuser
の名前が必要になる場合があります。
オブジェクトにアクセスするには、メソッドでthis
キーワードを使用できます。
this
値は「ドットの前」のオブジェクト、つまりメソッドの呼び出しに使用されるオブジェクトです。
例えば:
ユーザー = { にします 名前:「ジョン」、 年齢:30歳、 SayHi() { // 「this」は「現在のオブジェクト」です アラート(この名前); } }; user.sayHi(); // ジョン
ここでuser.sayHi()
の実行中、 this
の値はuser
になります。
技術的には、 this
使用せずに、外部変数を介してオブジェクトを参照することで、オブジェクトにアクセスすることも可能です。
ユーザー = { にします 名前:「ジョン」、 年齢:30歳、 SayHi() { アラート(ユーザー名); // 「this」ではなく「user」 } };
…しかし、そのようなコードは信頼できません。 user
別の変数 (例: admin = user
にコピーし、 user
別の変数で上書きすると、間違ったオブジェクトにアクセスすることになります。
以下にそれを示します。
ユーザー = { にします 名前:「ジョン」、 年齢:30歳、 SayHi() { アラート( ユーザー名 ); // エラーが発生する } }; 管理者 = ユーザーとします。 ユーザー = null; // わかりやすくするために上書きします admin.sayHi(); // TypeError: null のプロパティ 'name' を読み取れません
alert
内でuser.name
の代わりにthis.name
を使用すると、コードは機能します。
JavaScript では、 this
キーワードは他のほとんどのプログラミング言語とは異なります。オブジェクトのメソッドでなくても、あらゆる関数で使用できます。
次の例には構文エラーはありません。
関数sayHi() { アラート( this.name ); }
this
値は、コンテキストに応じて実行時に評価されます。
たとえば、ここでは同じ関数が 2 つの異なるオブジェクトに割り当てられており、呼び出しに異なる「this」が含まれています。
ユーザー = { 名前: "ジョン" }; let admin = { 名前: "管理者" }; 関数sayHi() { アラート( this.name ); } // 2 つのオブジェクトで同じ関数を使用します user.f = こんにちは; admin.f = こんにちは; // これらの呼び出しには this が異なります // 関数内の「this」は「ドットの前」のオブジェクトです user.f(); // ジョン (この == ユーザー) admin.f(); // 管理者 (この == 管理者) 管理者['f'](); // Admin (ドットまたは角括弧はメソッドにアクセスします – 関係ありません)
ルールは単純です。 obj.f()
が呼び出された場合、 this
f
の呼び出し中のobj
です。したがって、上の例ではuser
またはadmin
いずれかになります。
オブジェクトなしでの呼び出し: this == undefined
オブジェクトをまったく持たずに関数を呼び出すこともできます。
関数sayHi() { アラート(これ); } こんにちは(); // 未定義
この場合、 this
厳密モードではundefined
です。 this.name
にアクセスしようとすると、エラーが発生します。
非厳密モードでは、このような場合のthis
の値はグローバル オブジェクトになります (ブラウザ内のwindow
。グローバル オブジェクトの章で後ほど説明します)。これは"use strict"
歴史的な動作です。
通常、このような呼び出しはプログラミング エラーです。 this
関数内にある場合、オブジェクト コンテキストで呼び出されることが期待されます。
this
アンバインドした場合の結果
別のプログラミング言語を使用している場合は、おそらく、オブジェクト内で定義されたメソッドが常にそのオブジェクトを参照するthis
を持つ「バインドされたthis
」の考え方に慣れているでしょう。
JavaScript では、 this
「フリー」であり、その値は呼び出し時に評価され、メソッドが宣言された場所には依存せず、「ドットの前」にあるオブジェクトに依存します。
this
評価したランタイムの概念には、プラス面とマイナス面の両方があります。一方で、関数はさまざまなオブジェクトに対して再利用できます。一方で、柔軟性が高まると間違いが発生する可能性も高くなります。
ここで私たちの立場は、この言語設計の決定が良いか悪いかを判断することではありません。それをどのように扱うか、利益を得る方法、問題を回避する方法を理解します。
アロー関数は特別で、「独自の」 this
を持ちません。このような関数からthis
参照する場合、それは外側の「通常の」関数から取得されます。
たとえば、ここでarrow()
外側のuser.sayHi()
メソッドからthis
使用します。
ユーザー = { にします 名前:「イリヤ」、 SayHi() { let arrow = () =>alert(this.firstName); arrow(); } }; user.sayHi(); // イリヤ
これはアロー関数の特別な機能であり、実際に個別のthis
を用意するのではなく、外部コンテキストから取得する場合に便利です。 「アロー関数の再考」の章の後半で、アロー関数についてさらに詳しく説明します。
オブジェクトのプロパティに格納される関数を「メソッド」と呼びます。
メソッドを使用すると、オブジェクトがobject.doSomething()
のように「動作」できるようになります。
メソッドはオブジェクトをthis
として参照できます。
this
値は実行時に定義されます。
関数が宣言されると、 this
使用できますが、関数が呼び出されるまでthis
には値がありません。
関数はオブジェクト間でコピーできます。
関数が「メソッド」構文object.method()
で呼び出される場合、呼び出し中のthis
の値はobject
です。
アロー関数は特殊であることに注意してください。アロー関数にはthis
ありません。アロー関数内でthis
にアクセスすると、外部から取得されます。
重要度: 5
ここで、関数makeUser
オブジェクトを返します。
そのref
にアクセスすると結果はどうなりますか?なぜ?
関数 makeUser() { 戻る { 名前:「ジョン」、 参考:これ }; } let user = makeUser(); アラート( ユーザー参照名 ); // 結果はどうなりましたか?
答え: エラーです。
試してみてください:
関数 makeUser() { 戻る { 名前:「ジョン」、 参考:これ }; } let user = makeUser(); アラート( user.ref.name ); // エラー: 未定義のプロパティ 'name' を読み取れません
これは、 this
設定するルールがオブジェクト定義を考慮していないためです。電話をかけた瞬間だけが重要です。
ここで、 makeUser()
内のthis
の値はundefined
です。これは、「ドット」構文のメソッドとしてではなく、関数として呼び出されるからです。
this
値は関数全体に対して 1 つであり、コード ブロックとオブジェクト リテラルは影響しません。
したがって、 ref: this
実際には関数の現在のthis
を取得します。
関数を書き換えて、同じthis
undefined
値で返すことができます。
関数 makeUser(){ これを返します。 // 今回はオブジェクトリテラルはありません } alert( makeUser().name ); // エラー: 未定義のプロパティ 'name' を読み取れません
ご覧のとおり、 alert( makeUser().name )
の結果は、前の例のalert( user.ref.name )
の結果と同じです。
逆のケースは次のとおりです。
関数 makeUser() { 戻る { 名前:「ジョン」、 ref() { これを返します。 } }; } let user = makeUser(); アラート( user.ref().name ); // ジョン
user.ref()
はメソッドなので、これで動作します。そしてthis
の値は dot の前のオブジェクトに設定されます.
。
重要度: 5
3 つのメソッドを使用してオブジェクトcalculator
を作成します。
read()
2 つの値の入力を求め、それぞれa
およびb
という名前のオブジェクト プロパティとして保存します。
sum()
保存された値の合計を返します。
mul()
保存された値を乗算し、結果を返します。
計算機 = { にしましょう // ... コード ... }; 電卓.read(); アラート( 計算機.sum() ); アラート( calculator.mul() );
デモを実行する
テストを含むサンドボックスを開きます。
計算機 = { にしましょう 合計() { this.a + this.b を返します。 }、 mul() { this.a * this.b を返します。 }、 読む() { this.a = +prompt('a?', 0); this.b = +prompt('b?', 0); } }; 電卓.read(); アラート( 計算機.sum() ); アラート( calculator.mul() );
サンドボックス内のテストを含むソリューションを開きます。
重要性: 2
上り下りできるladder
オブジェクトがあります。
はしご = { にしましょう ステップ: 0、 上() { この.ステップ++; }、 下() { このステップ--; }、 showStep: function() { // 現在のステップを表示します アラート( this.step ); } };
ここで、複数の呼び出しを連続して行う必要がある場合は、次のように行うことができます。
はしご.up(); はしご.up(); はしご.down(); はしご.showStep(); // 1 はしご.down(); はしご.showStep(); // 0
次のように、 up
、 down
、およびshowStep
のコードを変更して呼び出しをチェーン可能にします。
はしご.up().up().down().showStep().down().showStep(); // 1、次に 0 を表示します
このようなアプローチは、JavaScript ライブラリ全体で広く使用されています。
テストを含むサンドボックスを開きます。
解決策は、すべての呼び出しからオブジェクト自体を返すことです。
はしご = { にしましょう ステップ: 0、 上() { この.ステップ++; これを返します。 }、 下() { このステップ--; これを返します。 }、 showStep() { アラート( this.step ); これを返します。 } }; はしご.up().up().down().showStep().down().showStep(); // 1、次に 0 を表示します
1 行に 1 つの呼び出しを記述することもできます。長いチェーンの場合は、より読みやすくなります。
ラダー 。上() 。上() 。下() .showStep() // 1 。下() .showStep(); // 0
サンドボックス内のテストを含むソリューションを開きます。