javascriptのthis参照を✅
どうしても解決しなかったエラーを二文字だけで解決してくださった普久原さんに感謝している23期スバルです。
今日は1週間に一度だけ訪れるチートデイです。
チートデイは外食と銭湯に行き、運動による肉体疲労と勉強による精神疲労を一気に癒します。
普段は沼(鶏胸肉 + 米 + 乾燥椎茸 + 乾燥わかめ + カレー粉)生活をしているだけに、楽しみな日なのですが、その人MVPリリース日が被ってしまいました。
疲れを取ったのも束の間、MVPリリースに向けて自分に鞭打って最終点検を行いました。
なんとかMVPリリースができてよかったです。
とはいえ、これからが本番なわけで。
皆さんの声を元に12月11日リリースに向けてPDCA回して良いポートフォリオにしていきたいです。
さて話は学習内容に戻り、今回はjsvascriptのthisをチェックしていきたいと思います。
thisとは
this
は読み取り専用のグローバル変数のようなものでどこにでも書けます。 加えて、this
の参照先(評価結果)は条件によって異なります。
this
の参照先は主に次の条件によって変化するそう。
- 実行コンテキストにおける
this
- コンストラクタにおける
this
- 関数とメソッドにおける
this
- Arrow Functionにおける
this
ひとつづつ見ていきたい。
実行コンテキストにおけるthis
トップレベル(もっとも外側のスコープ)にあるthis
は、実行コンテキストによって値が異なるそう。
実行コンテキストにおいてthisの違いは認識しづらいため、トップレベルでthis
を使うと混乱を生むことになりる。 そのため、コードのトップレベルにおいてはthis
を使うことはバッドプラクティスだそう。
それぞれの実行コンテキストにおける動作は以下の通り。
スクリプトにおけるthis
実行コンテキストが"Script"である場合、トップレベルのスコープに書かれた
this
はグローバルオブジェクトを参照する。
グローバルオブジェクトは実行環境ごとに異なる。
ブラウザのグローバルオブジェクト: window
オブジェクト
Node.jsのグローバルオブジェクト: global
オブジェクト
ブラウザでは、script
要素のtype
属性を指定していない場合は、実行コンテキストが"Script"として実行されます。 このscript
要素の直下に書いたthis
はグローバルオブジェクトであるwindow
オブジェクトとなる。
<script>
# 実行コンテキストは"Script"
console.log(this); // => window
</script>
モジュールにおけるthis
実行コンテキストが"Module"である場合、そのトップレベルのスコープに書かれたthis
は常にundefined
となる。
これは、Moduleの段階だとまだ参照する関数が定義されていないためである。
また、ブラウザで、script
要素にtype="module"
属性がついた場合は、実行コンテキストが"Module"として実行される。 このscript
要素の直下に書いたthis
はundefined
となる。
<script type="module">
# 実行コンテキストは"Module"
console.log(this); // => undefined
</script>
このように、トップレベルのスコープのthis
は実行コンテキストによってundefined
となる場合がある。
単純にグローバルオブジェクトを参照したい場合は、this
ではなくglobalThis
を使うそう。
関数とメソッドにおけるthis
関数を定義する方法は下記のようなものがある。
function
キーワードによる関数宣言と関数式- Arrow Functionなど
this
が参照先を決めるルールは、Arrow Functionとそれ以外の関数定義の方法で異なるため、そもそもの関数の種類を復習する必要がある。
関数の種類
関数を定義する方法は以下の三つがある。
# functionキーワードからはじめる関数宣言
function fn1() {}
#functionを式として扱う関数式
const fn2 = function() {};
# Arrow Functionを使った関数式
const fn3 = () => {};
それぞれ定義した関数は関数名()
と書くことで呼び出す事ができる。
# 関数宣言
function fn() {}
# 関数呼び出し
fn();
メソッドの種類
JavaScriptではオブジェクトのプロパティが関数である場合にそれをメソッドと呼ぶ。
メソッドと関数には以下のような違いがある。
- メソッドも含めたものを関数と言う
- 関数宣言などとプロパティである関数を区別する場合にメソッドと呼ぶ
メソッドを定義する場合には、オブジェクトのプロパティに関数式を定義するだけ。
const obj = {
# `function`キーワードを使ったメソッド
method1: function() {
},
# Arrow Functionを使ったメソッド
method2: () => {
}
};
これに加えてメソッドには短縮記法があり、
オブジェクトリテラルの中で メソッド名(){ /*メソッドの処理*/ }
と書くことで、メソッドを定義できる。
const obj = {
# メソッドの短縮記法で定義したメソッド
method() {
}
};
これらのメソッドは、オブジェクト名.メソッド名()
と書くことで呼び出す事ができる。
const obj = {
# メソッドの定義
method() {
}
};
# メソッド呼び出し
obj.method();
関数宣言や関数式におけるthis
まずは、関数宣言や関数式の場合。
次の例では、関数宣言で関数fn1
と関数式で関数fn2
を定義し、それぞれの関数内でthis
を返している。
定義したそれぞれの関数をfn1()
とfn2()
のようにただの関数として呼び出しているが、このとき、ベースオブジェクトはないことから、this
はundefined
となることが確認できる。
"use strict";
function fn1() {
return this;
}
const fn2 = function() {
return this;
};
# 関数の中の`this`が参照する値は呼び出し方によって決まる
# `fn1`と`fn2`どちらもただの関数として呼び出している
# メソッドとして呼び出していないためベースオブジェクトはない
# ベースオブジェクトがない場合、`this`は`undefined`となる
console.log(fn1()); // => undefined
console.log(fn2()); // => undefined
これは、関数の中に関数を定義して呼び出す場合も同じ挙動をする。
"use strict";
function outer() {
console.log(this); // => undefined
function inner() {
console.log(this); // => undefined
}
# `inner`関数呼び出しのベースオブジェクトはない
inner();
}
# `outer`関数呼び出しのベースオブジェクトはない
outer();
メソッド呼び出しにおけるthis
メソッドの場合は、JavaScriptではオブジェクトのプロパティとして指定される関数のことをメソッドと呼ぶため、そのメソッドが何かしらのオブジェクトに所属することになっている。
次の例ではmethod1
とmethod2
はそれぞれメソッドとして呼び出されている。 このとき、それぞれのベースオブジェクトはobj
となり、this
はobj
となる。
const obj = {
# 関数式をプロパティの値にしたメソッド
method1: function() {
return this;
},
# 短縮記法で定義したメソッド
method2() {
return this;
}
};
# メソッド呼び出しの場合、それぞれの`this`はベースオブジェクト(`obj`)を参照している。
# メソッド呼び出しの`.`の左にあるオブジェクトがベースオブジェクト
console.log(obj.method1()); // => obj
console.log(obj.method2()); // => obj
これを利用すれば、メソッドの中から同じオブジェクトに所属する別のプロパティをthis
で参照する事ができる。
const alcohol = {
alcoholName: "beer",
sayName: function() {
# `alcohol.fullName`と書いているのと同じ
return this.fullName;
}
};
#`alcohol.fullName`を出力する
console.log(alcohol.sayName()); // => "beer"
Run
このようにメソッドが所属するオブジェクトのプロパティを、オブジェクト名.プロパティ名
の代わりにthis.プロパティ名
で参照する事ができる。
オブジェクトは何重にもネストできるが、this
はベースオブジェクトを参照するというルールは同じになっている。
次のコードは、ネストしたオブジェクトにおいてメソッド内のthis
がベースオブジェクトであるobj3
を参照していることが読み取れる。
このときのベースオブジェクトはドットでつないだ一番左のobj1
ではなく、メソッドから見てひとつ左のobj3
となっている。
const obj1 = {
obj2: {
obj3: {
method() {
return this;
}
}
}
};
# `obj1.obj2.obj3.method`メソッドの`this`は`obj3`を参照
console.log(obj1.obj2.obj3.method() === obj1.obj2.obj3); // => true
感想
今日はしんどい1日だった、、
ポモドーロテクニックを教えていくださった井上さん、タイムクラウドのにしこさんに感謝!
次回は12月18日。楽しみ!