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メソッドはalligatorcrocの両方にアクセスできるようになりました。 Reptileprototypeにはこの方法があります。 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