ECMAScriptオブジェクトのスコープ

スコープとは変数の適用範囲を指します。

パブリック、プライベート、プロテクテッドスコープ

概念

伝統的な面向オブジェクトプログラミングでは、パブリックとプライベートスコープに注目されます。パブリックスコープ内のオブジェクト属性は外部からアクセス可能であり、つまり開発者がオブジェクトのインスタンスを作成した後、そのパブリック属性を使用することができます。一方、プライベートスコープ内の属性はオブジェクト内のみでアクセス可能であり、つまり外部世界にとってこれらの属性は存在しません。これは、クラスがプライベートな属性やメソッドを定義した場合、その子クラスもこれらの属性やメソッドにアクセスできないことを意味します。

プロテクテッドスコープもプライベートな属性やメソッドを定義するために使用されますが、これらの属性やメソッドはその子クラスからもアクセスできます。

ECMAScriptはパブリックスコープしかありません

ECMAScriptではスコープについてのECMAScriptの議論はほとんど意味がありません。なぜなら、ECMAScriptではスコープは一種類しか存在しないからです - パブリックスコープ。ECMAScriptのすべてのオブジェクトのすべての属性やメソッドはパブリックです。したがって、自分自身のクラスやオブジェクトを定義する際には特に注意が必要です。全ての属性やメソッドはデフォルトでパブリックであることを思い出してください!

推奨される解決策

多くの開発者がオンラインで効果的な属性スコープパターンを提案し、ECMAScriptのこの問題を解決しました。

プライベートスコープが不足しているため、開発者が規約を定め、どの属性やメソッドがプライベートであるべきであるかを決定しました。この規約は、属性の前後にアンダースコアを付けることを定めます:

obj._color_ = "blue";

このコードでは、属性 color はプライベートです。注意して、アンダースコアは属性がパブリック属性であることを変えることはありませんが、他の開発者にこの属性をプライベートと見なすべきであることを示しています。

一部の開発者は、単一のアンダースコアでプライベートメンバーを示すのが好きです。例えば:obj._color。

静的作用域

静的作用域で定義された属性とメソッドはいつでも同じ場所からアクセスできます。Javaでは、クラスはオブジェクトをインスタンス化しなくても属性とメソッドにアクセスできます。例えば、java.net.URLEncoder クラスの encode() 関数は静的メソッドです。

ECMAScript には静的作用域はありません

厳密には、ECMAScript には静的作用域はありません。ただし、構造関数には属性とメソッドを提供できます。覚えていますか?構造関数はただの関数です。関数はオブジェクトであり、オブジェクトには属性とメソッドがあります。例えば:

function sayHello() {
  alert("hello");
}
sayHello.alternate = function() {
  alert("hi");
}
sayHello();		//「hello」を出力
sayHello.alternate();		//「hi」を出力

TIY

ここでは、method alternate() は実際には function sayHello のメソッドです。sayHello() を通常の関数のように呼び出して「hello」を出力することもできますし、sayHello.alternate() を呼び出して「hi」を出力することもできます。それでも、alternate() は sayHello() の共有作用域内のメソッドであり、静的メソッドではありません。

キーワード this

this の機能

ECMAScript では、最も重要な概念の1つはキーワード this の使用法であり、それはオブジェクトのメソッドに使用されます。キーワード this は常にそのメソッドを呼び出すオブジェクトを指します。例えば:

var oCar = new Object;
oCar.color = "red";
oCar.showColor = function() {
  alert(this.color);
};
oCar.showColor();		//「red」を出力

TIY

上記のコードでは、キーワード this がオブジェクトの showColor() メソッドに使用されています。この環境では、this は oCar に等しいです。以下のコードは上記のコードと同じ機能を持っています:

var oCar = new Object;
oCar.color = "red";
oCar.showColor = function() {
  alert(oCar.color);
};
oCar.showColor();		//「red」を出力

TIY

this を使用する理由

なぜ this を使用するのでしょうか?なぜなら、オブジェクトをインスタンス化する際には、開発者がどのような変数名を使用するかを決定することができないからです。this を使用することで、同じ関数を複数の場所で再利用できます。以下の例を考えてみてください:

function showColor() {
  alert(this.color);
};
var oCar1 = new Object;
oCar1.color = "red";
oCar1.showColor = showColor;
var oCar2 = new Object;
oCar2.color = "blue";
oCar2.showColor = showColor;
oCar1.showColor();		//「red」を出力
oCar2.showColor();		//「blue」を出力

TIY

上記のコードでは、まずthisでshowColor()関数を定義し、その後oCar1とoCar2という2つのオブジェクトを作成します。oCar1のcolor属性は「red」、oCar2のcolor属性は「blue」に設定されます。2つのオブジェクトは属性showColorに設定され、元のshowColor()関数を指しています(注意:これは名前の問題ではなく、1つはグローバル関数であり、もう1つはオブジェクトの属性です)。各オブジェクトのshowColor()を呼び出すと、oCar1の出力は「red」、oCar2の出力は「blue」になります。これは、oCar1.showColor()を呼び出すとき、関数内のthisキーワードがoCar1に等しいため、oCar2.showColor()を呼び出すとき、関数内のthisキーワードがoCar2に等しいためです。

注意、オブジェクトの属性を参照する際には、thisキーワードを使用する必要があります。例えば、以下のコードでは、showColor()メソッドは実行されません:

function showColor() {
  alert(color);
};

オブジェクトやthisキーワードを使用せずに変数を参照しない場合、ECMAScriptはそれをローカル変数またはグローバル変数として認識します。その後、この関数はcolorという名前のローカルまたはグローバル変数を探しますが見つかりません。その結果はどうなるでしょう?この関数は「null」を警告メッセージで表示します。