※当方駆け出しエンジニアのため、間違っていることも多々あると思いますので、ご了承ください。また、間違いに気付いた方はご一報いただけると幸いです。
【JavaScript】JavaScriptにおけるクロージャーとは
↑こちらの記事に以下の通り記載しました。
「JavaScriptのスコープには、どの識別子がどの変数を参照するかが静的に決定されるという性質があります。
つまり、どこでinside関数を呼び出そうが、inside内のcountは、outside直下のcountを参照するということですね。
「this」という特別なキーワードだけは、呼び出し元によって動的に参照先が変わります。」
例えば、
function (){
retrun this;
}
このような関数があった時、返却されるthisは呼び出し元によって変化します。
原則として、thisが参照するのは、ざっくりというと**「呼び出し元自体」**です。
まず、**「呼び出し先が異なる」**とはどういう意味なのか。
まず、以下のような内部処理にthisを含む関数があるとします。
function (){
console.log(this.name);
}
上記関数が、以下のようなhuman1, human2オブジェクトのgetNameプロパティーとして定義されているとします。それぞれ、プロパティーに
nameプロパティー(valueは文字列), getNameプロパティ(valueはメソッド(関数))が定義されています。
human1 = {
name: "Taro",
getName: function () {
console.log(this.name);
}
}
human2 = {
name: "Ken",
getName: function () {
console.log(this.name);
}
}
ちなみにイメージしやすいように、メソッドプロパティーを key : value で記載しましたが、
メソッドの定義部分は以下のように省略してもかけます。
human2 = {
name: "Ken",
getName() {
console.log(this.name);
}
}
話を戻しまして
それぞれのメソッドを呼び出してみます。
human1.getName(); ・・・※1
//Taro
human2.getName(); ・・・※2
//Ken
それぞれ、呼び出し元によって異なる文字列が返ってきました。
どのような動きとなっているのか。
※1の場合、getName()メソッドの呼び出し元は、 human1ですね。
つまり、メソッド内部のthisには、 呼び出し元自体の human1オブジェクトが参照されるわけですね。
イメージとしては、↓のような感じですね。
human1 = {
name: "Taro",
getName: function () {
console.log(human1.name);
}
}
再帰的に、呼び出し元オブジェクトが内部のthisから参照されます。
human2は
human2 = {
name: "Taro",
getName: function () {
console.log(human2.name);
}
}
ですね。
なぜ、thisから呼び出し元が参照されるかというと、イメージとしては呼び出し時に暗黙的に、仮引数に、呼び出し元オブジェクトを渡す感じです。
どういうことかといいますと、下のようなイメージです。
human1 = {
name: "Taro",
getName: function (x) { //暗黙的に呼び出元オブジェクトを仮引数xに渡す。
this = x; //thisにxを格納。
console.log(this.name);
}
}
console.log(human1.getName(human1)); //暗黙的に呼び出し元オブジェクトを引数に渡す。
thisを用いることによって、メソッド内部の定義を共通化できるのです。
これによりクラスからインスタンスを作った際に、それぞのインスタンスが持つインスタンス変数を参照することができます。
呼び出し元の種類によって、参照先が異なります。
場所 | Thisの参照先 |
---|---|
トップレベル(関数の外) | グローバルオブジェクト |
関数 | グローバルオブジェクト |
コンストラクター | 生成したインスタンス |
メソッド | 呼び出し元のオブジェクト |
イベントリスナー | イベントの発生元 |
call/applyメソッド | 引数で指定されたオブジェクト |
原則は全て、呼び出し元がthisに入るということです。
call/applyメソッドについては、thisの参照先を指定することができます。
一つ一つ例を見ていきます。
###関数の外の場合
console.log(this);
//window
呼び出し元であるグローバルオブジェクト(windowオブジェクト)が参照されています。
###関数の場合
function test() {
console.log(this);
};
test();
//window
同様に呼び出し元であるグローバルオブジェクト(windowオブジェクト)が参照されています。
ちなみに、グローバルオブジェクトがもつプロパティを参照した場合は、当然その値が参照されます。
function test() {
console.log(this.document);
};
test();
//#document
//<html>
// <body>
// <script src="practice.js"></script>
// </body>
//</html>
###コンストラクターの場合
class Member {
constructor(name) {
this.name = name;
}
}
let member = new Member("Taro")
console.log(member);
//{name: Taro}
let member2 = new Member("Ken");
console.log(member2);
//{name: Ken}
まず、new演算子でインスタンスが作成されて、そのインスタンスがコンストラクターを呼び出します。
(その時の引数はmemberでは Taro)
thisには呼び出し元のmemberインスタンスが参照されているので
this.name = name;
//これはつまり
member.name = "Taro"
ということで、memberインスタンスのnameプロパティにTaroが入ります。
###メソッドの場合
メソッドの場合は、上記の例通りなので割愛
###イベントリスナーの場合
<div id='checkThis'></div>
↑ の上にマウスが乗ったら、色が黄色く変わるというシンプルなコードです。
let elem = document.getElementById('checkThis');
elem.addEventListener('mouseover', function () {
this.style.backgroundColor = "Yellow";
}, false);
この場合、thisはイベント発生元である elem が参照されます。
よって、そのelemのスタイルに背景色黄色を設定し変色させます。
##call/applyメソッドの場合
call/applyメソッドの場合だけ、特殊でthisは呼び出し元が参照されますが、それを
異なるオブジェクトを指定してしまおうというものです。
human2 = { name: "Ken" };
human1 = {
name: "Taro",
getName: function () {
console.log(this.name);
}
}
human1.getName();
//Taro
human1.getName.call(human2);
//Ken
普通にgetName()を実行すると、当然human1がthisから参照されます。
しかし、callメソッドで引数にhuman2を渡すと、thisの参照先がhuman2となっています。
callメソッドとapllyメソッドの違いは、関数に渡す引数の渡し方の違いだけです。
func.call("thisに指定するオブジェクト", 引数, 引数, 引数)
//callは、funcに渡す引数を列挙
func.aplly("thisに指定するオブジェクト", [引数, 引数, 引数])
//apllyは配列で渡す。
続きはこちら
(【JavaScript】JavaScriptの 「this」とは 2 (thisの問題点と解決策))[https://qiita.com/sho_U/items/e10edd4c6c142fb37ea3]