JavaScriptのオブジェクトは、他の言語と比較して、常に少し不適合でした。
JavaScriptのオブジェクト指向の初心者もよく混乱します:
- なぜJavaScriptにはオブジェクトの概念があるのに、他の言語のようなクラスがないのでしょうか;
- JavaScriptのオブジェクトには自由に属性を追加できるのに、他の言語ではできないのはなぜですか?
ある議論では、JavaScriptは「オブジェクト指向言語」ではなく「オブジェクトベース言語」であると強調されています。この声明はしばらくの間広く流布されましたが、実際、私がこれまでに会った中でこの声明を持っている人は誰も、「オブジェクト指向とオブジェクトベースをどう定義するか」という質問に答えることができませんでした。
実際、オブジェクトベースやオブジェクト指向という形容詞は、JavaScript標準のすべてのバージョンに登場します。
JavaScript標準のオブジェクトベースの定義を見てみましょう。"言語とホストのインフラはオブジェクトによって提供され、JavaScriptプログラムは互いに通信するオブジェクトの集まりである "とあります。
ここでのポイントは、弱いオブジェクト指向を表現することではまったくなく、むしろ言語にとってのオブジェクトの重要性を表現することです。
さて、この記事では、JavaScriptにおけるオブジェクト指向やオブジェクトオリエンテーションが実際にどのようなものなのかを理解してもらおうと思います。
オブジェクト指向とは?
とはいえ、オブジェクトはコンピュータの分野で無から生み出された概念ではなく、人間の思考パターンに従った抽象的なものであることを認識すべきです。
ではまず、人間の考え方における「モノ」とは何なのかを見てみましょう。
物体の概念は、プログラミング・ロジックでよく使われる「価値」や「過程」の概念よりもずっと前の、幼児期に形成されます。幼児期には、まずリンゴが食べられるものであることが認識され、次にリンゴはすべて食べられるものであることが認識されます。
グレイディ・ブーチは、『オブジェクト指向分析と設計』という本の中で、人間の認識という観点から、オブジェクトは以下のいずれかに当てはまるべきだと主張しています:
- 触ったり見たりできるもの;
- 人間の知性が理解できるもの;
- 思考や行動の指針となるもの。
オブジェクトの自然な定義ができれば、プログラミング言語でオブジェクトを記述することができます。さまざまなプログラミング言語において、設計者はまた、抽象的な方法でオブジェクトを記述するために、さまざまな言語機能を使用してきました。 最も成功した流派は、オブジェクトを記述するために「クラス」を使用することであり、C++やJavaなどの一般的なプログラミング言語を生み出しました。
一方、JavaScriptはその初期において、プロトタイプというもっと冷たいアプローチを選びました。これが、先ほど「なじまない」と言った理由のひとつです。
JavaScriptの創始者であるBrendan Eichは、JavaScriptが導入されたとき、残念なことに社内政治的な理由でJavaを模倣するよう経営陣から命じられました。この "言語機能は、JavaScriptを "よりJavaらしく "するために導入されました。
ES6の登場により、多くのJavaScriptプログラマーが、JavaScriptをプロトタイプシステムをベースとしたクラスベースのプログラミングに近づけようとし、PrototypeJSやDojoなどのいわゆる「フレームワーク」が数多く生まれました。
実際、JavaScriptの風変わりな方言のようなものになり、互いに排他的なコミュニティまで生まれました。
実行時の観点からオブジェクトについて語る場合、JavaScriptが実際に動作するモデルについて語ることになります。
しかし、幸いなことに、実行時の観点からは、どの言語でも実行時にはクラスの概念が弱まるため、こうした「クラスベースの設備」に悩まされることはありません。
JavaScriptがどのようにオブジェクトモデリング用に設計されているかを理解することから始めましょう。
JavaScript オブジェクトの特徴
私見ですが、どのプログラミング言語を使うにしても、まずはオブジェクトの本質的な特徴を理解するようにすべきです。要約すると、オブジェクトには次のような特徴があります。
- 物体は一意に識別できます。同じ物体が2つあっても、同じ物体ではありません。
- オブジェクトには状態があり、同じオブジェクトでも状態が異なることがあります。
- オブジェクトには振る舞いがあります。つまり、振る舞いの結果として変化する可能性のあるオブジェクトの状態です。
最初の特徴である、オブジェクトが一意に識別可能であることについて見てみましょう。一般的に、様々な言語におけるオブジェクトの一意な識別子は、メモリアドレスで表現されます。 オブジェクトはメモリアドレスによって一意に識別されるため、一意な識別子を持ちます。
だから、JavaScriptプログラマは、任意の異なるJavaScriptオブジェクトは、実際には互いに等しくないことを知って、あなたは次のコードを見ることができます、o1とo2は、一見すると、2つの同一のオブジェクトですが、出力は偽です。
var o1 = { a: 1 };
var o2 = { a: 1 };
console.log(o1 == o2); // false
オブジェクトの2つ目と3つ目の特徴である「状態」と「振る舞い」については、言語によって抽象的に表現する用語が異なります。例えば、C++では「メンバ変数」と「メンバ関数」と呼びますが、Javaでは「プロパティ」と「メソッド」と呼びます。C++では「メンバ変数」と「メンバ関数」と呼ばれ、Javaでは「プロパティ」と「メソッド」と呼ばれます。
JavaScriptでは、ステートとビヘイビアは統一され、"プロパティ "として抽象化されています。 関数がJavaScriptの特別なオブジェクトとして設計されていることを考えると、JavaScriptのビヘイビアとステートはプロパティとして抽象化することができます。
次のコードは実際に、通常の属性と、属性としての関数の例を示しています。oはオブジェクト、dは属性、関数fも属性で、同じように書かれていなくても、dとfはJavaScriptの通常の属性にすぎません。
var o = {
d: 1,
f() {
console.log(this.d);
}
};
つまり、まとめると、JavaScript ではオブジェクトの状態と振る舞いは実際にはプロパティに抽象化されています。Java を使ったことがある人なら、設計思想に多少の違いはあるにせよ、どちらもオブジェクトの基本的な特徴である ID、状態、振る舞いをうまく表現していることを知って驚くことはないでしょう。
オブジェクトの基本的な特徴の実装に基づき、JavaScriptにおけるオブジェクトの特徴は、JavaScriptが実行時にオブジェクトの状態や振る舞いを変更する能力をユーザーに与えるため、オブジェクトが非常に動的であることだと考えています。
一例として、JavaScriptでは実行時にオブジェクトにプロパティを追加することができます。これは、ほとんどのクラスベースの静的オブジェクト設計とはまったく異なります。Javaや他の言語を使ったことがある人なら、私と同じように感じるでしょう。
次のコードは、実行時にオブジェクトにプロパティを追加する方法を示しています。 最初にオブジェクトoを定義し、定義が終わったらプロパティbを追加していますが、これはまったく問題ありません。
var o = { a: 1 };
o.b = 2;
console.log(o.a, o.b); //1 2
抽象度を高めるため、JavaScript のプロパティは他の言語よりも複雑に設計されており、データプロパティとアクセッサプロパティの両方を提供しています。
JavaScript 2種類のオブジェクト・プロパティ
JavaScriptでは、プロパティは単なる名前と値ではありません。
まず、最初のタイプの属性であるデータ属性から説明しましょう。これは他の言語における属性の概念に近いものです。データ属性には4つの特徴があります。
- value: 属性の値。
- writable: プロパティに値を割り当てることができるかどうかを決定します。
- enumerable: for in がプロパティを列挙できるかどうかを決定します。
- configurable: 属性の削除や特徴値の変更が可能かどうかを指定します。
ほとんどの場合、データ属性の値だけを気にすれば十分です。
二つ目の属性はアクセサー属性で、これも四つの特徴を持っています。
- getter: 関数または未定義。属性の値を取得するときに呼び出されます。
- setter: プロパティの値を設定するときに呼び出される関数または未定義。
- enumerable: for in がプロパティを列挙できるかどうかを決定します。
- configurable: 属性の削除や特徴値の変更が可能かどうかを指定します。
アクセッサ属性は、属性が読み書きされる際にコードを実行させます。これにより、ユーザは属性の書き込み時と読み込み時に全く異なる値を得ることができます。
通常、プロパティを定義するために使用されるコードは、書き込み可能、列挙可能、および設定可能がすべてtrueにデフォルト設定されているデータプロパティを生成します。次のコードに示すように、組み込み関数getOwnPropertyDescripterを使用してこれを確認できます:
var o = { a: 1 };
o.b = 2;
//aとbはどちらもデータ属性である
Object.getOwnPropertyDescriptor(o,"a") // {value: 1, writable: true, enumerable: true, configurable: true}
Object.getOwnPropertyDescriptor(o,"b") // {value: 2, writable: true, enumerable: true, configurable: true}
ここでは、属性を定義するために2つの構文が使用されています。属性を定義した後、JavaScript APIを使用して属性を表示します。この方法で定義された属性はデータ属性であり、writeable、enumerable、configurableはすべてtrueの値にデフォルト設定されていることがわかります。
プロパティの特性を変更したり、アクセサ・プロパティを定義したりするには、以下の例に示すように、Object.definePropertyを使用します:
var o = { a: 1 };
Object.defineProperty(o, "b", {value: 2, writable: false, enumerable: false, configurable: true});
//aとbはどちらもデータ属性だが、特徴値が変わっている。
Object.getOwnPropertyDescriptor(o,"a"); // {value: 1, writable: true, enumerable: true, configurable: true}
Object.getOwnPropertyDescriptor(o,"b"); // {value: 2, writable: false, enumerable: false, configurable: true}
o.b = 3;
console.log(o.b); // 2
ここでは、Object.defineProperty を使用してプロパティを定義し、プロパティの書き込み可能および列挙可能を変更できるようにしています。
また、次のコードに示すように、getキーワードとsetキーワードを使って、オブジェクトを作成するときにアクセサ・プロパティを作成することもできます:
var o = { get a() { return 1 } };
console.log(o.a); // 1
アクセッサ・プロパティはデータ・プロパティとは異なり、アクセスされるたびにゲッターまたはセッター関数を実行します。ここでは、ゲッター関数は1を返すので、o.aは毎回1を取得します。
このように、JavaScriptオブジェクトのランタイムは、文字列やシンボルをキーとし、データ属性特徴値やアクセッサ属性特徴値を値とする、実際には「属性のコレクション」であると理解されます。
オブジェクトは属性のインデックス構造です。上のオブジェクトoを例にとると、"a "がキーであると想像できます。
とはいえ、モノの特性を理解していれば、冒頭で私が投げかけた質問を理解するのは難しいことではありません。
JavaScriptはオブジェクト指向ではない」という言葉が存在する理由も理解できるでしょう。JavaScriptのオブジェクト設計は、現在主流のクラスベースのオブジェクト指向とは大きく異なるからです。
実際、このようなオブジェクトシステムの設計は珍しいのですが、JavaScriptは完全なランタイムオブジェクトシステムを提供しており、ほとんどのオブジェクト指向プログラミングパラダイムを模倣することができるため、オーソドックスなオブジェクト指向言語でもあります。
JavaScriptの言語標準は、JavaScriptがオブジェクト指向言語であることも明確にしていますが、JavaScriptが非常に動的なオブジェクトシステムであるからこそ、標準がそう言えるのだと思います。
ですから、他の言語を機械的に模倣するのではなく、その設計思想を理解した上で、その能力を十分に発揮させるべきです。
まとめ
JavaScriptのオブジェクトを理解するためには、「クラスベースのオブジェクト指向」に関連する知識をすべて頭から空っぽにして、オブジェクトに対する人間の単純な認識と、言語に依存しないオブジェクト指向の基礎理論に立ち返る必要があります。
この記事では、オブジェクトの基本的な理論から始め、オブジェクトに関する基本的な概念を明らかにし、JavaScript オブジェクトの設計思想を分析します。次に、実行時の観点から JavaScript オブジェクトの設計を紹介します。
JavaScriptのオブジェクトは、既存の「オブジェクト」の見方で考える人が多く、その結果、当然ながら「切り口はごちゃごちゃ」になってしまいます。
転載元:Geek Time、著作権侵害の削除はご連絡ください。