Edited at

JavaScriptのthisがよくわからないので徹底的に調べてできるだけわかりやすく解説してみる


JavaScriptのthisがわからない?偶然!わたしもー

わたし 「あれ、このエレメントどうやって取り出すんやろ・・・せや、console.log(this)してみよ・・・」

わたし 「あれ? undefined? なんでや? 3行上で同じことやるとちゃんと表示されるやん・・・」

...

....

.....

......

わたし 「なんでやねん!?thisわけわからんわ!?

という状況に直面したので、JavaScriptのthisをちゃんと知っておこうと思い、アウトプットもかねて記事を書くことにします。


thisとは

thisとは、thisが所属している関数を実行するオブジェクトのことである。

...

...

...🤔 🤔 🤔 ??? (大丈夫、書いてる私も意味わかってないで)


thisには2パターンある

thisには2パターンあります。どこで使うかによって、thisが指す内容が変わってきます。

まずこの2パターンを今自分がなんとなくこの場でジャンプしたら地震が起きるんじゃないかくらいの「なんとなく」レベルで理解しましょう。


パターン ① (this = 所属しているオブジェクト)

「メソッド」の場合、thisは「所属しているオブジェクト」そのものを指す

ここでいうメソッドとは、「関数がオブジェクトの中に存在しているもの」のことです。


パターン ② (this = グローバルオブジェクト)

「通常の関数(オブジェクトの中に存在していない関数)」の場合、thisはグローバルオブジェクトである「ブラウザ内のWindowオブジェクト」&「node内のグローバルオブジェクト」を指す

...

....

...🤔 🤔 🤔 ???

わかりにくいと思うので、一つ一つ例をみていきたいと思います。


パターン ① (メソッドでthisを使った場合はオブジェクトを返す)

「メソッド(関数がオブジェクトの中に存在しているもの)」の場合、thisは「所属しているオブジェクト」そのものを指す

これをなんとなく頭の片隅で意識しながら、例をみてみましょう。


例 ①

最初に、titleプロパティを持ったmangaオブジェクトを生成

→ read()メソッドの中でconsole.log(this)する

    const manga = {

title: 'Fullmetal Alchemist',
read() {
console.log(this);
}
}
manga.read();

すると、コンソールにはmangaオブジェクトが表示されます。

なぜかというと、このreadメソッドは「mangaオブジェクトに所属している」からです。

この場合のthisは、mangaオブジェクトそのものを指しています。

image.png


例 ②

mangaオブジェクトの後にメソッドを追加したとしても、thisはmangaオブジェクトを参照します。

mangaオブジェクトの後に、closeメソッドを追加

→ closeメソッドの中でconsle.log(this)をする

    const manga = {

title: 'Fullmetal Alchemist',
read () {
console.log(this);
}
}

manga.close = function() {
console.log(this);
}
manga.close();

すると、thisは例①と同じような結果である、mangaオブジェクトを返します。

closeメソッドはmangaオブジェクトのメソッドだからです。

image.png

ここまでが、メソッドの中でthisを使った場合でした。


パターン ② (通常の関数でthisを使った場合はグローバルオブジェクトを返す)

今度は、通常の関数でthisを使った場合どうなるかみてみます。

ルールは、「通常の関数(オブジェクトの中に存在していない関数)」の場合は、thisはグローバルオブジェクトである「ブラウザ内のWindowオブジェクト」&「node内のグローバルオブジェクト」を指す、ですね。


例 ①

readManga()という関数を定義

→関数内でconsole.log(this)する

    const manga = {

title: 'Fullmetal Alchemist',
read() {
console.log(this);
}
}

function readManga(){
console.log(this);
}

readManga();

すると今度は、ブラウザのWindowsオブジェクトが表示されました!

「Window」から始まるのがWindowsオブジェクトであることを指してますね。

image.png

このように関数の中でthisを使うと、thisはWindowオブジェクトを指すことがわかります。


例 ② コンストラクタ関数の場合はちょっと特殊

コンストラクタ関数の場合はちょっと特殊。thisはオブジェクトを指すようになります。

manga関数を生成

→ newして新しいmangaオブジェクトを作る

    const manga = {

title: 'Fullmetal Alchemist',
read() {
console.log(this);
}
}

function Manga(title){
this.title = title;
console.log(this);
}

// 新しいmangaオブジェクトを生成後、空っぽのオブジェクトを生成する -> {}
const doraManga = new Manga('Doraemon');

すると今度は、Windowオブジェクトの代わりに、「mangaオブジェクト」が返されました。

image.png


なぜ?

new演算子は「空っぽのオブジェクト」を生成します。そして、「this」をその空っぽのオブジェクトの中に放り込む仕組みです。

そのため、この「doraManga(生成されたmangaオブジェクト)」は、1行目〜6行目のmangaオブジェクトは全く別物になります。

結果、Windowオブジェクトではなく、mangaオブジェクトが表示されました。


例 ③

今度は下記をやってみたらどうなるか見てみます。

categoriesというプロパティを作って、配列を当てはめる

→ showCategoriesメソッドを作る

→ メソッド内でthis.categories.forEachし、その中でconsole.log(categories)する

    const manga = {

title: 'Fullmetal Alchemist',
categories: ['love', 'philosophy', 'adventure'],
showCategories() {
this.categories.forEach(function(categories){
console.log(categories);
});
}
};

manga.showCategories();

すると、こんな風にcategoriesが表示されます。ここまでは予想通り、オッケーですね。

image.png

でももし、titleをそれぞれのcategoriesの横に表示させたいとしたら、どうすればいいでしょうか?

試しに、this.titleしてみましょう。

    const manga = {

title: 'Fullmetal Alchemist',
categories: ['love', 'philosophy', 'adventure'],
showCategories() {
this.categories.forEach(function(categories){
console.log(this.title, categories);
});
}
};

manga.showCategories();

undefinedになっちゃう...><;

image.png

this.titleをthisにすると...?

    const manga = {

title: 'Fullmetal Alchemist',
categories: ['love', 'philosophy', 'adventure'],
showCategories() {
this.categories.forEach(function(categories){
console.log(this, categories);
});
}
};

manga.showCategories();

image.png

Windowオブジェクトが返されました!

「このthisは「mangaオブジェクト」の中にいるから、mangaオブジェクトを指すべきでは?なぜwindowオブジェクを指しているの?」と思うかもしれません。

このthisは、forEachというcallback関数の中にいますね。この関数は通常の関数です。要するに、mangaオブジェクトのメソッドではありません。

結果、ルール2が適用されてthisはグローバルオブジェクトを指すのです。


でも、どうやってmangaのタイトルを、categoriesの隣に表示すれば良いの?🤔

今回は、forEachメソッドを使っているので、2つめの引数を設定することができます。

第一引数にcallback関数、第二引数に「this」を入れてあげることで、thisがmangaオブジェクトを指してくれます。


例えばこんな風に

    const manga = {

title: 'Fullmetal Alchemist',
categories: ['love', 'philosophy', 'adventure'],
showCategories() {
this.categories.forEach(function(categories){
console.log(this, categories);
}, this);
}
};

manga.showCategories();

だだーん。

グローバルオブジェクトではなく、mangaオブジェクトが表示されました。

この場合、thisはforEachメソッドの中ではなく、showCategoriesメソッドに所属しています。

そのため、mangaオブジェクトを指しているのです。

image.png


では、thisをthis.titleに変えると?

    const manga = {

title: 'Fullmetal Alchemist',
categories: ['love', 'philosophy', 'adventure'],
showCategories() {
this.categories.forEach(function(categories){
console.log(this.title, categories);
}, this);
}
};

manga.showCategories();

きれい!満足や!

image.png


ただし

forEachのようにすべてのJavaScriptのメソッドが、第2引数を入れてあげられるわけではないので、注意!


ちなみに

こんな書き方もできます。この書き方が一番簡単かもしれませんね。

  const manga = {

title: 'Fullmetal Alchemist',
categories: ['love', 'philosophy', 'adventure'],
showCategories: function() {
this.categories.forEach((category) => {
console.log(this, category);
});
}
};

manga.showCategories();

image.png


まとめ

これで君をJavaScript this使いに任命する!

P.S 私は鋼の錬金術師の大ファンです。