〽️ 「メソッドかどうか」がポイント。
〽️ 「解らない部分」をピンポイントで解説。
JavaScriptコードの「?」をまとめたページはこちら⬆︎。
@1@ メソッドのthisは、所属先のオブジェクト。
@2@ それ以外のthisは、windowオブジェクト。
@3@ 主な例外:コンストラクタのthis、アロー関数のthis。
こんにちは、「ふ」です。
今回はJavaScriptのthisキーワードについて、お話ししていきます。
このthisキーワード、
・記述場所によって参照するものがころころ変わる
・法則を知ったとしても、現在のthisがどの状況にいるかわかりづらい。
などの理由から、完全理解はかなり困難だとされています。
でも大丈夫です。冒頭でも示した⬇︎の基本法則。
@1@ メソッドのthisは、所属先のオブジェクトを指す。
@2@ それ以外のthisは、windowオブジェクトを指す。
@3@ 主な例外:コンストラクタのthis、アロー関数のthis。
これさえ踏まえておけば、さまざまなケースにおける事象を紐解いて理解していくことができますよ。
またJavaScriptでthisキーワードを使う場面は、そんなに多くはありません。なので基本的な使い方とよく遭遇する例外について知っていれば、さほど不自由することもないでしょう。
@1@ メソッドのthisは、所属先のオブジェクトを指す。
JavaScriptのthisは、オブジェクトを参照するキーワードです。
基本法則の1つ目、メソッドのthis。これは所属するオブジェクトを指します。
const a = { num:10, 「「3//⬇︎これはメソッド」」 func:function() { console.log("hello"); } };
メソッドとは、オブジェクトのプロパティになっている関数のことでしたね。
上のサンプルコード。オブジェクトaのプロパティのうち、1つ目の値は数値の10ですが、2つ目の値は関数形式になっています。このようなものがメソッドです。
「メソッド?」となった方は⬇︎の記事を参考にしてください。
〽️ 名前空間の独立とthisキーワード。
先のメソッドでは"hello"を出力するものでしたが、"hello"の代わりにthisを出力するようにしてみましょう。
const a = { num:10, func:function() { console.log(「「1this」」); } }; a.func(); 「「3▶︎ {num:10,func:f}」」
オブジェクトの外からa.funcを実行すると、オブジェクトaの内容が出力されました。
「this」キーワードが、オブジェクトa自体を指していることがわかります。
const a = { num:10, 「「3//⬆︎これにアクセスしたい」」 .... }
メソッドのthisキーワードから、オブジェクト内の他のプロパティを呼び出すこともできます。
1つ目のnumにアクセスしてみましょう。
const a = { num:10, func:function() { console.log(「「1this.num」」); } }; a.func(); 「「3▶︎ 10」」
thisキーワードから辿って、numプロパティの値を取り出せました。
ここまでthisを使ってオブジェクトaを呼び出してきました。
が、直接オブジェクト名を記述して、
const a = { num:10, func:function() { console.log(「「4a」」.num); } };
としても、フツーに自身のオブジェクトにアクセスできます。なぜあえてthisを使ったりするのでしょうか?
consoleする内容を「this.num」ではなく「a.num」とした状態で、オブジェクトaを複製・編集する場合を考えてみましょう。
const a = { num:10, func:function() { console.log(a.num); } }; const b = {...a,「「2num:20」」};
aの複製としてオブジェクトbを作り、numの値を20に上書きしました。
b.func(); 「「3 ▶︎ 10」」
そのうえで、新しく作ったオブジェクトbのfuncメソッドを実行したところ、上書きしたはずのnumの値が「10」と出力されます。
どういうことでしょうか?
console.log(b); 「「3▶︎ { num:20, func:function() { console.log(「「4a」」.num); } }」」
bの内容を見てみます。
numプロパティの値は確かに「20」となっているのですが、funcメソッドでconsole.logする内容が依然として「a.num」となってしまっています。
具体的なオブジェクト名「a」としたものを複製したため、bのfuncを実行しても「a.num」を参照してしまっていたのでした。「10」と出力されてしまったのも、このせいです。
aのfuncの参照先を「this」に戻して、やり直してみましょう。
const a = { num:10, func:function() { console.log(「「1this」」.num); } }; const b = {...a,num:20}; a.func(); 「「3▶︎ 10」」 b.func(); 「「3▶︎ 20」」
こうすれば、「console.log(this.num)」がコピーされるため、funcを実行したときにはa、bともにそれぞれ自身のオブジェクトにアクセスしてくれるようになります。
thisキーワードを使うと「自身のオブジェクトを指す」処理を使いまわすことができるんですね。オブジェクト複製などを行うときは、thisを使っておいたほうが都合がいいんです。
先ほど、スプレッド構文を使ってオブジェクトを複製・上書きしました。その仕組みについては⬇︎の記事を参考にしてください。
〽️ 最短の方法を見つけました。
@2@ それ以外のthisは、windowオブジェクトを指す。
それでは基本法則の2つ目。
メソッドの中のthisキーワードは、属しているオブジェクトを参照していました。そして、それ以外の場所にあるthisは、windowオブジェクトを指します。
<script> const A = function() { console.log(this); } <script>
<script>直下のグローバル環境にある、関数A。
thisを出力する処理になっていますが、このthisはメソッドの中ではなく、フツーの関数の中にいます。
A(); 「「3▶︎ Window {window: Window, self: Window, document: document, name: '', location: Location, …}」」
関数Aを実行したところ、consoleにはwindowオブジェクトが表示されました。
このようにメソッド以外の中にいるthisは、windowオブジェクトを参照します。
windowオブジェクトは、ブラウザが生成するすべての母体となるオブジェクトです。
詳しくは⬇︎の記事を参考にしてください。
〽️ Jsの構造を、そろそろはっきりさせましょう。
windowオブジェクトを参照するthisは、多用するべきではありません。なぜなら、windowsオブジェクトのプロパティを直接操作することになるからです。
console.log(this.console === window.console); 「「3▶︎ true」」
例えば、「this.console」に対して何か処理をおこなうと、そのままwindowオブジェクトに含まれるconsoleオブジェクトが編集されてしまいます。
this.console = "AAA"; console.log("hello"); 「「3▶︎ error:console.log is not a function」」
this.consoleとし、文字列を代入してみました。
その後console.logメソッドを使ったところ「console.logは関数じゃないよ」とエラーになってしまいます。consoleオブジェクトが編集されて、壊れてしまったのですね💧
これは極端な例です。consoleのようなメジャーなオブジェクトであれば、さすがに気付くでしょう。が、windowオブジェクトにはさまざまな内容が含まれています。thisキーワードを多用していると、知らないうちにwindowオブジェクトのプロパティを変更してしまうかもしれません。
グローバルを指すthisを不用意に使ったがために、プログラム全体に支障が起きてしまわないよう、じゅうぶん注意してください。
@1@ メソッドのthisは、所属先のオブジェクトを指す。
@2@ それ以外のthisは、windowオブジェクトを指す。
メソッドのthis/それ以外のthisについて、ここまでみてきました。
「さあこれでthisをマスターしたぞ!」となるかもしれませんが、実際にコーディングしてみると、これがなかなかむづかしい。
というのも、「今このthisを取り巻く環境ははメソッドなのか?それ以外なのか?」をちゃんと見分けるのにはもう少し理解が必要になってくるからです。
いくつかのサンプルを通して、メソッド/それ以外を判別できるように練習しておきましょう。
const c = { value:this };
⬆︎のコードを見てください。
「value」は、オブジェクトcのプロパティです。このthisはどうなっているのでしょうか。オブジェクト内のthisだから、オブジェクトcを指しているはず?
console.log(c.value); 「「3▶︎ Window{....}」」
console.logしてみると、windowオブジェクトとなりました。
オブジェクトのプロパティであっても、メソッド以外であれば、windowオブジェクトを参照してしまいます。この点には注意してください。
const d = { num:100 };
次のケースです。
オブジェクトdのnumプロパティは、「100」となっています。このnumを参照して何かをしたいとき。
let C = 「「1d.num」」 + 100; console.log(C); 「「3▶︎ 200」」
オブジェクトの外部からのアプローチは、簡単です。
フツーに呼び出して、処理をするだけ。ここではdのnumに100を足しています。
では、オブジェクトの内部からこのような操作をしたいときは、どーする?
const d = { num:100, C:this.num+100 } console.log(d.C); 「「3▶︎ NaN」」
内部でプロパティCを定義し、値を「this.num+100」としてみます。しかしこれはうまくいきません。
Cはdのプロパティではあるものの、メソッドではありません。先ほども述べたように、メソッドではないプロパティのthisからは、自身のオブジェクトは参照できないのです。
このthisは、windowオブジェクトを参照することになります。すると「オブジェクト+100」は計算ができません。したがって「NaN」と出力されたのです。
もしオブジェクトdの中でプロパティを操作したいときには、メソッドに取り込むようにしましょう。
const d = { num:100, D:function() { return this.num+100; } } console.log(d.D()) 「「3▶︎ 200」」
メソッドDをつくり、その中でthis.numを使いました。
メソッド内のthisは自身のオブジェクトにアクセスしてくれます。外部からメソッドを実行すると、ちゃんと計算してくれるようになりました。
今度はthisをつかった関数を、メソッド化してつかうサンプルです。
function E() { console.log(this); } E(); 「「3▶︎ window {....}」」
グローバル下でthisを出力する、関数Eです。実行すると当然、windowオブジェクトとconsoleに表示されます。
ただしこのグローバル関数であるEを「メソッド化」させると、内部のthisは親オブジェクトを参照するようになります。
const e = { F:「「1E」」 }; e.F(); 「「3▶︎ {F:f}」」
オブジェクトeを作り、プロパティFの値にさっきのフツー関数Eをそのまま指定します。そのあとメソッドFを実行すると、eのメソッドとして関数Eが実行されます。
メソッド内の処理にある「this」は自身の所属オブジェクトであるeを参照するようになります。この「F:E」という記述は、⬇︎のように書いたのと同じことになります。
const e = { F:「「1function() { console.log(this);」」 } };
フツー関数Eの内容をそのままもらってきて、メソッドにしてしまっているんですね。
「一般の関数をメソッド化し、親のオブジェクトやプロパティを操作する」。
便利な使い道がありそうですね。
もう1ついきます。オブジェクトのメソッド内で、thisを含んだフツー関数を呼び出すとどうなるのでしょうか。
const G = function() { console.log(this); } const f = { H:function() {「「4G()」」;} }
グローバル下で定義した関数G。これを、オブジェクトfのメソッドであるHの中で呼び出しています。
f.H(); 「「3▶︎ window{....}」」
Hを実行したところ、windowオブジェクトが出力されます。Gの中のthisはオブジェクトfを参照してくれません。
これはなぜなのか。1つ前のサンプルと比較してみましょう。
const e = { F:E };
1つ前のサンプルでは、プロパティFの値に直接関数Eが割り当てられています。
関数Eの内容がまるごとプロパティFに直結しているため、メソッド化ができていたのです。
const f = { H:function() {G();} }
では今回のばあいはどうでしょうか。
メソッドHはあくまで「function( ){ .... }」全体がその対象です。関数Gを呼び出し実行しているのは、メソッドの中の1処理にすぎません。
つまりGは、メソッドではないのです。したがってその中のthisキーワードは「メソッド以外」ということで、windowオブジェクトを参照するようになったのです。
最後までお読みくださり、ありがとうございました。
今回はJavaScriptのthisの基本法則について紹介してきました。ここまでみてくれた方であれば、
・メソッドが自身のオブジェクト、それ以外はwindow。
・今このthisはメソッド?メソッドじゃない?
〜2つの知識と判断力がしっかりと備わっていることでしょう。ご自分でも簡単なコードを書いて、実験してみてください。
@3@ 主な例外:コンストラクタのthis、アロー関数のthis。
冒頭にあげた3つの法則のうち、@3@についてのお話しがまだでしたね。
thisキーワードには大きな例外が2つ、存在します。
function obj1(a) { this.a = a; }
1つ目はコンストラクタのthis。
⬆︎のような「this.a = a」という謎の表記です。
こちらについては、以前リリースしたコンストラクタの記事で詳しく解説していますので、参考にしてください。
〽️ 「this x = x」の解明。
もう1つの大きな例外は「アロー関数のthis」です。
const obj2 = { b:()=>console.log(this) } obj2.b(); 「「3▶︎ Window{....}」」
アロー関数をオブジェクトのプロパティに指定すると、その中のthisはなんと。オブジェクトのスコープを無視して、windowオブジェクトを参照するのです。
なぜこのような特性が用意されているのか?そのメリットは?
アロー関数のthisについても、別記事を用意させていただくことにしました。参考にしてください⬇︎。
〽️ 外側の関数スコープから特定。
この記事を読んで、みなさんのthisに対するモヤモヤが解消されれば、幸いです。
ではまた〜 ♪
ベクターグラフィック、web、ガジェットなど。役立つ情報や観ていてたのしいページを書いていきたいと思います。