JavaScriptのオブジェクト、プロトタイプ、クラス
JavaScriptのほとんどすべてがオブジェクトであるという事実を考慮すると、オブジェクト指向のJavaScriptコードは、他のオブジェクト対応言語とは大きく異なります。 JSオブジェクトシステムは、代わりにプロトタイプベースのオブジェクトシステムです。
C ++のバックグラウンドから来て、私はオブジェクト指向プログラミングパラダイムを知っていて、オブジェクトとクラスがどのように機能するかについて非常に厳格な考えを持っていました。 Javaのような他の言語への露出は、この考えをさらに確立するように思われました。 これらの言語には、オブジェクトとクラスがどのように機能するかについて独自のセマンティクスがあります。 新しいユーザーにとって、Javascriptはかなりの啓示です。
まず最初に、JavaScriptオブジェクトは作成方法が大きく異なります。 クラスの要件はありません。 オブジェクトインスタンスは、新しい演算子を使用して作成できます。
let Reptile = new Object() {
// ...
}
または関数コンストラクターを使用
function Reptile() {
// ...
}
第二に、JavaScriptオブジェクトは非常に柔軟性があります。 従来のオブジェクト指向言語ではプロパティの変更またはプロパティスロットのみが許可されていますが、JavaScriptではオブジェクトがプロパティとメソッドを変更できます。 すなわち JavaScriptオブジェクトには、プロパティスロットとメソッドスロットの両方があります。
発見で最初に思ったのは「ええ、自由!」でした。 しかし、これにはコストが伴いました。JavaScriptのプロトタイププロパティを理解する必要があります。 プロトタイプの知識は、JavaScriptでオブジェクト指向システムの類似性を実装したい開発者にとって不可欠です。
すべてのJavaScriptオブジェクトは、Object
コンストラクターから作成されます。
var Reptile = function(name, canItSwim) {
this.name = name;
this.canItSwim = canItSwim;
}
また、prototype
を使用すると、オブジェクトコンストラクターに新しいメソッドを追加できます。これは、Reptile
のすべてのインスタンスに次のメソッドが存在することを意味します。
Reptile.prototype.doesItDrown = function() {
if (this.canItSwim) {
console.log(`${this.name} can swim`);
} else {
console.log(`${this.name} has drowned`);
}
};
Reptile
のオブジェクトインスタンスを作成できるようになりました。
// for this example consider alligators can swim and crocs cannot
let alligator = new Reptile("alligator", true);
alligator.doesItDrown(); // alligator can swim
let croc = new Reptile("croc", false);
croc.doesItDrown(); // croc has drowned
Reptile
オブジェクトのprototype
が継承の基礎になり、doesItDrown
メソッドはalligator
とcroc
の両方にアクセスできるようになりました。 Reptile
のprototype
にはこの方法があります。 prototype
プロパティは、そのすべてのインスタンス間で共有され、特定のインスタンスの__proto__
プロパティを介してアクセスできます。
現在、メソッドスロットが存在し、共通のprototype
インスタンスプロパティがすべてのインスタンスで共有されているため、C++の人々にとって非常に奇妙ないくつかの非常に巧妙なトリックが可能です。
croc.__proto__.doesItDrown = function() {
console.log(`the croc never drowns`);
};
croc.doesItDrown(); // the croc never drowns
alligator.doesItDrown(); // the croc never drowns
1つのインスタンスのprototype
プロパティまたはメソッドを変更すると、オブジェクトのすべてのインスタンスが影響を受けます。 これは、私たちも削除する可能性があることを意味します。 溺死にうんざりしているワニは、これを行う可能性があります。
delete croc.__proto__.doesItDrown
alligator.doesItDrown();
//TypeError: alligator.doesItDrown
// is not a function
今では誰も泳ぐことができません。
これは、prototype
がJavaScriptのオブジェクトシステムにとってどれほど基本的であり、他のオブジェクト指向言語の人々にとって非常に不快感を与える可能性があるかを示すためのばかげた例です。
ES6構文では、JavaScriptにクラスを作成する機能が提供されています。
ただし、真のクラスの概念はJavaScriptには存在しませんが、prototype
を介してエミュレートされ、クラス構文はその周りの単なる構文糖衣です。 したがって、この動作を理解することは、ES6クラスの利便性と制限を理解するために重要です。
新しいclass
構文では、Reptile
は次のように定義されます。
class Reptile {
constructor (name, canItSwim) {
this.name = name;
this.canItSwim = canItSwim;
}
doesItDrown () {
if(this.canItSwim)
console.log(`${this.name} can swim`);
else
console.log(`${this.name} has drowned`);
}
}
let alligator = new Reptile("alligator", true);
alligator.doesItDrown(); //alligator can swim
これは、prototype
ユーザー向けのオファーに新しいものがないことを意味するものではありません。インスタンスの作成にnew
キーワードを必須にするなど、ES6クラスを使用することでいくつかの落とし穴を回避できます。
let croc = Reptile("croc", false);
//TypeError: Class constructor Reptile cannot be invoked without 'new'
これは、オブジェクトのプロパティとメソッド(通常はグローバルスコープまたはウィンドウオブジェクト)を使用するときに間違ったコンテキストにアクセスするのを防ぐため、実際には良いことです。
結論は
JavaScriptは今のところ確かに本当にプライベートなメンバーのような機能を欠いていますが。 プロトタイプがC++/ Javaのような他のオブジェクト指向言語のクラスに非常に似ているのではなく、クラス構文を介してオブジェクトを作成するようになりました。
PS。 JavaScriptクラスで真にプライベートなメンバーを作成するためのTC39への提案がありました。それをここに従って、意見を投稿してください。 次のリビジョンに含まれるとしたら、次のようになります。
class Foo {
#a; #b; // # indicates private members here
#sum = function() { return #a + #b; };
}
// personally this format reminds me of $variable in PHP.
// I'm not sure if that's a good thing