お笑い好きによるJavaScriptの「this」まとめ

More than 1 year has passed since last update.


誰に向けて


  • 全てのJS初心者に向けて

  • 技術的なことばをできるだけ避けて学びたい人


経緯

JavaScript初学者です。

パッションが湧いてきて、ここ数日間猛烈に学んでいます。

「this」の扱いが比較的とっつきづらかったので、

注意すべき点を整理しました。


全体像(2018/05/28現在)

全体像は以下です。

image.png

注意すべき点をまとめて

「アロー呼び出しABC」

などとゴロよく呼べば

頭のメモリを節約できそうです。

(フロントエンドは諸行無常なのでいつまで持つかわかりませんが)

では、それぞれの項目を細かく見ていきます。


呼び出し


1.関数呼び出し

宣言した関数を関数名()で直接呼び出します。

関数の宣言方法は以下の記事でまとめました。


https://qiita.com/konkipiano/items/ba9df345c17e739ecba2

お笑い好きによるJavaScriptの関数宣言まとめ


thisグローバルオブジェクト、つまりWindowを指します。


tsukkomi_func.js

//関数宣言

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

//関数呼び出し
tsukkomi(); //->Window {…}



※間違えやすい点

オブジェクトの中でも、

関数呼び出しであればthisは問答無用で「Window」を指します。

関数の宣言場所がオブジェクトの中でも外でも関係ありません。


tsukkomi_func_obj_out.js

//外部関数宣言

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

//オブジェクト宣言
var geinin = {
tsukkomi: function(){
//外部関数呼び出し
tsukkomi();
}
}

//関数呼び出し
tsukkomi(); //->Window {…}
//メソッド呼び出し(中で関数呼び出し)
geinin.tsukkomi(); //Window {…} thisは"geinin"ではなく"Window"を指す



tsukkomi_func_obj_in.js


//オブジェクト宣言
var geinin = {
tsukkomi: function(){
//内部関数宣言
function tsukkomi(){
console.log(this);
}
//内部関数呼び出し
tsukkomi();
}
}

//関数呼び出し
tsukkomi(); //->Window {…}
//メソッド呼び出し(中で関数呼び出し)
geinin.tsukkomi(); //Window {…} thisは"geinin"ではなく"Window"を指す



2.メソッド呼び出し

オブジェクトのフィールドに宣言されている関数を、

オブジェクト名.関数名()で呼び出します。

thisはメソッドの親オブジェクトを指します。


tsukkomi_method.js

var geinin = {

tsukkomi: function(){
console.log(this);
}
}
geinin.tsukkomi(); //->{tsukkomi: ƒ} メソッドの親オブジェクト"geinin"を指す



3.コンストラクタ呼び出し

コンストラクタ関数をnewで呼び出してインスタンス生成します。

このとき、コンストラクタ関数の中のthisは生成されるインスタンスを指します。


tsukkomi_const.js

//コンストラクタ関数宣言

function Geinin(boke,tsukkomi){
this.boke = boke;
this.tsukkomi = tsukkomi;
}

//コンストラクタ関数をnewで呼び出し
var kojima = new Geinin("oi","kojimadayo");

console.log(kojima.boke); //->"oi"
console.log(kojima.tsukkomi); //->"kojimadayo"



※間違えやすい点

コンストラクタ関数をnewなしで呼び出す場合、「関数呼び出し」になります。

つまり、thisは「Window」を指します。


tsukkomi_const_func.js

//コンストラクタ関数宣言

function Geinin(boke,tsukkomi){
this.boke = boke;
this.tsukkomi = tsukkomi;
}

//コンストラクタ呼び出し…?いえ、newが無いので「関数呼び出し」です
var kojima = Geinin("oi","kojimadayo");

//newを忘れたkojimaはすごく怒られる
console.log(kojima.boke); //->怒られる
console.log(kojima.tsukkomi); //->怒られる

//そしてWindowにポジションを取られる
console.log(window.boke); //->"oi" windowが"oi"とボケてしまう
console.log(window.tsukkomi); //->"kojimadayo" windowが"kojimadayo"とツッコんでしまう



ABC(apply/bind/call)

apply/bind/callの頭文字を取りました。

これらの共通点は、「thisを書き換える」ということです。

目的別にわけると、

thisを書き換えて関数を実行する「apply」と「call」

thisを書き換えて関数を返す「bind」

となります。


AとC (apply/call)はthisを上書きして関数を実行する

applyとcallはどちらも、

thisを上書きして関数を実行します。


joyman_apply.js

var joymanIketani = {

tsukkomi: "nanda koitsu~",
doTsukkomi: function() {
console.log(this.tsukkomi);
}
};

var fujimoto = {
tsukkomi: "nanda koitsu~~~~~~~~~~~~!!!!!!"
}

joymanIketani.doTsukkomi(); //->"nanda koitsu~" 池谷が持ってる"tsukkomi"が適用される
joymanIketani.doTsukkomi.apply(fujimoto); // ->"nanda koitsu~~~~~~~~~~~~!!!!!!" 藤本が持ってる"tsukkomi"が適用される


上はapplyを使ったコードです。

これをcallで書き換えるには、applyをcallに書き換えるだけです。


joyman_call.js

var joymanIketani = {

tsukkomi: "nanda koitsu~",
doTsukkomi: function() {
console.log(this.tsukkomi);
}
};

var fujimoto = {
tsukkomi: "nanda koitsu~~~~~~~~~~~~!!!!!!"
}

joymanIketani.doTsukkomi(); //->"nanda koitsu~" 池谷が持ってる"tsukkomi"が適用される
joymanIketani.doTsukkomi.call(fujimoto); // ->"nanda koitsu~~~~~~~~~~~~!!!!!!" 藤本が持ってる"tsukkomi"が適用される



ちなみにこのサンプルコードで使っている

ジョイマン池谷さんとFUJIWARA藤本さんの絡みについてはこちら

https://www.youtube.com/watch?v=vkWjYgXpZJE



※applyとcallはじゃあ何が違うのか

ブレるので、このページでは説明を省きます。

また今度まとめます。


お笑い好きによる「apply」と「call」の違い

https://qiita.com/konkipiano/items/{TODO:記事を書いたらここにリンク貼る}



B (bind)はthisを上書きして関数を返す

すでにあるオブジェクトのthisを書きかえて関数を返します。


joyman_bind.js

var joymanIketani = {

tsukkomi: "nanda koitsu~",
doTsukkomi: function() {
console.log(this.tsukkomi);
}
};

var fujimoto = {
tsukkomi: "nanda koitsu~~~~~~~~~~~~!!!!!!"
}

var fujimotoIketaniDoTsukkomi = joymanIketani.doTsukkomi.bind(fujimoto) // ->関数を代入する
fujimotoIketaniDoTsukkomi(); //-> "nanda koitsu~~~~~~~~~~~~!!!!!!" この変数がthisをfujimotoに書き換えたdoTsukkomi関数を持ってる



アロー関数

アロー関数は、以下のように宣言する関数です。



arrow.js

let pikotaro = (pen,fruits) => {

return fruits + pen;
} //処理が1つの場合、{}を省略できる。

console.log(pikotaro('pen','apple')); //->applepen


参考:

お笑い好きによるJavaScriptの関数宣言まとめ

https://qiita.com/konkipiano/items/ba9df345c17e739ecba2


アロー関数で関数宣言すると、

宣言した時点でthisを固定することができます。

ちょっと何言ってるかわからないので、

通常の関数宣言とアロー関数による関数宣言を比べてみます。


HollywoodZakoShisho_arrow.js

// hanmar kanmarをグローバルなボケとして宣言

boke = "hanmar kanmar";

// 台本ボケをする関数
function doBoke() {
console.log(this.boke);
}

// zakoshishoがボケる関数
doZakoshiBoke = () => {
console.log(this.boke); // この時点でthisがwindowに固定される!
}

// この時点ではどちらのthis.bokeも"hanmar kanmar"を指す
doBoke(); //->"hanmar kanmar"
doZakoshiBoke(); //->"hanmar kanmar"

// 台本に"ai tuima te~n"と書いてあるお笑い番組
var desuyo_tv = {
boke: "ai tuima te~n",
geinin: doBoke,
zakoshi: doZakoshiBoke
};

// 台本に"chikusho"と書いてあるお笑い番組
var koume_tv = {
boke: "chikusho",
geinin: doBoke,
zakoshi: doZakoshiBoke
}

// desuyo_tvの進行
desuyo_tv.geinin(); //->"ai tuima te~n" 台本通りボケる
desuyo_tv.zakoshi(); //->"hanmar kanmar" 台本にセットされたボケを無視する

// koume_tvの進行
koume_tv.geinin(); //->"chikusho~" 台本通りボケる
koume_tv.zakoshi(); //->"hanmar kanmar" 台本にセットされたボケを無視する


通常の関数宣言で作成されたdoBoke関数は、

desuyo_tvでもkoume_tvでも

ちゃんと台本通り

"ai tuima te~n"



"chikusho"

とボケています。

こんな番組ないですが。

つまり、thisが呼ばれる番組によって変わることがわかります。

一方で、

アロー関数で宣言した

doZakoshiBoke関数は、

どちらの番組のbokeも無視して

"hanmar kanmar"

とボケています。

つまり、呼ばれる番組に関わらずthisがWindowに固定されていることがわかります。

これがアロー関数によるthisの特徴です。


まとめ

JavaScriptのthisはアロー呼び出しABCに注意します(2018/5/17現在)。

技術書を読むと

"thisを束縛する"

などのようなカッコイイ表現がたくさんありました。

ですが、この記事では「頭良く見られたい」という欲を必死におさえて

自分にとってイメージしやすい表現を使いました。

見識のある方からすると「なんだ??」という部分があると思います。

そういう場合はコメント頂けると嬉しいです。


参考にさせていただいたページ

JavaScriptの「this」は「4種類」??

https://qiita.com/takeharu/items/9935ce476a17d6258e27

【JavaScript】アロー関数式を学ぶついでにthisも復習する話

https://qiita.com/mejileben/items/69e5facdb60781927929

Javascriptのbind関数と部分適用 〜 JSおくのほそ道 #015

https://qiita.com/mejileben/items/69e5facdb60781927929