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

The prototypeReptile オブジェクトが継承の基礎になりました。 doesItDrown メソッドは両方にアクセス可能です alligatorcroc なぜなら prototypeReptile この方法があります。 The 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 ユーザーの場合、ES6クラスを使用することで、いくつかの落とし穴を回避できます。 new インスタンスの作成に必須のキーワード。

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