Movatterモバイル変換


[0]ホーム

URL:


LoginSignup
1596

Go to list of users who liked

1581

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

JavaScript の this を理解する多分一番分かりやすい説明

Last updated atPosted at 2017-07-29

JavaScript のthis は、(他のプログラム言語から見ると) ちょっと面白い挙動に見えることがあります。

先日、このthis の挙動について、会社の同僚が説明してくれたのですが、これまで聞いた説明の中で一番分かりやすいと感じたので、頑張って日本語で説明してみます。

分かりにくかったら、多分それは私の技量不足。

thisfunction の関係

function が基準スコープになるのがまず一点。

そのfunction をどう呼ぶかで変わるのかがもう一点。

それを踏まえて……

thisfunction を呼んだ時の. の前についているオブジェクトを指している

と理解できるというのが、同僚の説明でした。

. が省略された場合はグローバルオブジェクトになります (non-strict モード時)。 strict モードではundefined になります。(@ryo511 さん、ご指摘感謝)

js_this_and_func.png

サンプルコード

これだけだとナンノコッチャかもしれないので、具体例を……

functiontest(){console.log(this)}

this の内容をコンソールに表示するだけの、なんてことない関数です。

これをブラウザ上で呼び出すと……

test()// => Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}

のようになります。

呼び出し時に. がないので、this はグローバルオブジェクト、ブラウザではWindow オブジェクトになります (non-strict モード)。

この関数test を特定のオブジェクトに結びつけます。

functiontest(){console.log(this)}varobj={}obj.test=test

その上で、obj.test() を呼び出すと、thisobj になります。

obj.test()// => {test: ƒ}

関数test を呼び出す際、. がついていて、obj. となっているので、関数test の中ではthis =obj となるという訳です。

逆に、あるオブジェクトに結びついているものをグローバルスコープで呼び出すこともできます。

varobj={test:function(){console.log(this)}}vartest=obj.testtest()// => Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}

test()obj の関数test を呼び出していることになりますが、呼び出し時に. がついていないので、グローバルオブジェクトがthis になります (non-strict モード)。

上のサンプルコードでは、分かりやすくしていますが、例えば、setTimeout などでobj.test をコールバックとして渡すような場合でも同様のことが起きています。

メソッドチェーン

複数のfunction を数珠つなぎで呼び出すメソッドチェーンは. の前が関数になりますが、この場合、その関数が返すオブジェクトを参照することになります。

関数でreturn を省略したり、return 単独で呼ぶとundefined が返ってくるので、メソッドチェーンは利用できません。

varobj={test:function(){returnthis},alert:function(msg){console.log(msg)}}vartest=obj.testobj.test().alert("hello")// => hello とコンソールに表示test().alert("hello")// => アラート表示

関数alert の呼び出しに. があって、その前に関数test があります。

関数test の返り値はthis なので、そのtest の呼び出し方によって参照されるオブジェクトによって関数alert の結果が変わります。

callapply

callapply を使って関数を呼び出すと. に前につけるオブジェクトを指定することができます。

functiontest(){console.log(this)}varobj={name:"obj"}test()// => Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}test.call(obj)// => {name: "obj"}

test.call(obj)obj.test() に等しくなります (ただし、objtest というメソッドは追加されません)。

コンストラクタ

function に対して、new 構文を利用して、オブジェクトを作成することができます。

これは大雑把に言ってしまえば、call の変形構文と考えることができます。

例えば、以下のコード……

varobj=newfunction(){this.name="obj"console.log(this)// => {name: "obj"}}

この時、function が呼ばれますが、 this はグローバルオブジェクトでもundefined でもありません。

new を使うと、新規にオブジェクトを作成して、それに対して、call を使ってfunction を呼び出して、関数内部でreturn this するような動作になります。

つまり……

varobj=function(){this.name="obj"console.log(this)// => {name: "obj"}returnthis}.call({})

と同じ動作と考えることができます。

これは無名関数の場合ですが、名前付き関数 (例えばfunction Test()) の場合、内部でもう少し追加処理が入ります。 ただし、this の挙動に関しては、基本的に同様に考えることができると思います。

bind

bind はこの. ルールの挙動を変化させて、強制的にあるオブジェクトと結びつけます。

functiontest(){console.log(this)}varobj={name:"obj"}varcheck=test.bind(obj)check()// => {name: "obj"}

関数check の呼び出しには. がついていませんが、bind されているので、呼び出し時にobj. が付く形となり、関数test の中でthis =obj になります。

関数の中の関数

. はあくまでも関数呼び出し時に参照されるものです。

varobj={test:function(){console.log(this)// *1functiontest(){console.log(this)// *2}test()}}obj.test()

のようなコードがあった時、

*1 ではobj がコンソールに表示され、*2 ではグローバルオブジェクト (例えば、Window) が表示されます (non-strict モード時)。

*2 の関数呼び出しに. がないからですね。

varobj={test:function(){console.log(this)// *1functiontest(){console.log(this)// *2}test.call(this)}}obj.test()

call を使えば (apply でもいいです)、どっちもobj になります。

まとめ

thisfunction を呼んだ時の. の前についているオブジェクトを指している

  1. 関数呼び出しの. の前のオブジェクトがthis になる
  2. . を省略するとグローバルオブジェクトを参照する (non-strict モード)
    • strict モードではundefined になる
  3. call,apply を使うと. の前のオブジェクトを指定できる (コンストラクタはこの変形型)
  4. bind を使うと. の前のオブジェクトを強制できる

(補足)アロー関数について

当記事では、あえて ES2015 から導入されたアロー関数について触れていませんでした。

アロー関数でのthis の振る舞いは (他のプログラム言語から見て) 比較的自然で分かりやすい気がします。

最初に触れたとおり

  • function が基準スコープになる

のが JavaScript の大きな特徴です (var のスコープもこれに準じます)。

そのため、function を入れ子にした途端this の指すものが変わってきてしまうのが一番の混乱の要因です。当記事では、そこに焦点を当てて説明しています。

アロー関数は入れ子にしても、アロー関数の中で参照されているthis が指すものは変わりません。そのアロー関数がキャプチャされたときのthis を保持します。

1596

Go to list of users who liked

1581
5

Go to list of comments

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1596

Go to list of users who liked

1581

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?


[8]ページ先頭

©2009-2025 Movatter.jp