フーノページ





JavaScript、アロー関数のthisについて。

〽️ 外側の関数スコープを見ればよい。










JavaScript、サンプルコードの「これって何?」

〽️ 「解らない部分」をピンポイントで解説。

JavaScriptコードの「?」をまとめたページはこちら⬆︎。





アロー関数のthis。


@1@ 外側の関数スコープから見た「this」を参照する。
@2@ 関数スコープの中にいなければ、windowオブジェクトを指す。



基本法則をおさえよう。


こんにちは、「ふ」です。
前回の記事では、JavaScriptのthisの基本的な性質についてお話ししました。

JavaScript、thisの基本法則。

〽️ 「メソッドかどうか」がポイント。

JavaScriptのthisキーワードは、「メソッドの場合にはそのオブジェクト、それ以外はwindowオブジェクトを指す」というのが基本なのですが、大きな例外も存在します。その1つが、アロー関数の中にあるthisです。

const func = (引数) => 処理

アロー関数のthisは、「定義時の関数スコープから見たthisを参照する」という性質を持っています。通常のthisとは違うふるまいをするので、扱う際には注意が必要です。

「関数スコープ?」

となった方もいるかもしれません。この記事ではアロー関数のthisを理解するために必要な関数スコープ/ブロックスコープの説明も含めて、解説していくとします。 また「アロー関数でthisを使うことのメリットは?」についても述べていきます。

「アロー関数とは?」となった方は、⬇︎の記事を参考にしてください。

JavaScriptの矢印「=>」〜これはアロー関数というものです。

〽️ 使い方とメリットについて解説。





オブジェクトのスコープと関数スコープ。


オブジェクトのスコープと関数スコープについて確認しておきます。

<script>
const a = 10;
    const b = 「「1{」」
        a:20
    「「1}」」;
</script>

⬆︎のコードのうち、const bの波括弧{ }の中がオブジェクトのスコープです。
見てのとおり「オブジェクトbの内部」ということですね。

<script>〜</script>直下のグローバル空間で定数aを作っていますが、オブジェクトbのプロパティにもaというものがあります。
この「a」という名前の重複は、errorにはなりません。それぞれが置かれているスコープが別のものだからです。

定数aが置かれているのは、JavaScriptのグローバルなスコープ内です。プロパティaは、オブジェクトbのスコープ内です。アクセスする際には、

console.log(a);
「「3▶︎ 10」」
console.log(b.a);
「「3▶︎ 20」」

というようになります。定数aには単なる「a」、プロパティaには「b.a」とオブジェクト名をつなげる必要があります。この制約があるために、「a」という命名が重複しても問題はないのです。
オブジェクトのスコープである{ }は、ブロックスコープと呼ばれています。

次は関数スコープ。

「「1function A() {」」
    let a = 30;
    let b = 40;
「「1}」」

関数スコープは、関数の内側の範囲のことです。
内部で変数aを作っていますが、これもグローバル空間の定数aと衝突することはありません。

function A() {
    let a = 30;
    let b = 40;
    console.log(b) 「「3// OK」」
}

console.log(b) 「「4// error」」

関数スコープ内の値には、スコープ内部からでしかアクセスすることはできません。
⬆︎のコードではスコープ内では変数bにアクセスできているものの、外部であるグローバル空間からアクセスしようとするとエラーになります。
スコープ内で宣言した定数/変数には「内部からしかアクセスできない」という点でグローバル空間との差別化ができているんですね。

const B = 「「1function(」」b「「1) {」」
    return b+5;
「「1}」」

関数スコープには、引数の部分も含まれます。
⬆︎の引数bは、関数Bのスコープ内にあることになります。

console.log「「1(」」"hello"「「1)」」;

オブジェクトのメソッドも、関数スコープを持っていることになります。
console.logはconsoleオブジェクトのlogメソッドですが、引数部分にある「hello」という文字列はlogメソッドの、関数スコープの中にいます。

ここまでオブジェクトのスコープと、関数スコープについて知りました。
そしてアロー関数のthisは、この2つのスコープに対して通常のthisキーワードとは違った挙動をみせます。



ブロックスコープは通り抜ける。


通常。メソッドのthisは、自身のオブジェクトを参照するものでした。

const a = {
    A:function(){console.log(「「1this」」)}
};

a:A();
「「3▶︎ {A:f}」」

⬆︎のコードで、プロパティAはaのメソッドです。
「this」を出力する処理になっていますが、これを実行するとオブジェクトaの内容がコンソールに表示されます。


ところがアロー関数のthisは、そうはなりません。

const b = {
    B:()=>console.log(「「4this」」)
};

同じくthisを出力するメソッドを、アロー関数形式で書いてみました。

b.B();
「「3▶︎ Window{....}」」

が、実行してみると。メソッド内のthisであるにもかかわらず、windowオブジェクトを参照しています。

アロー関数のthisはその対象を探しにいくときに、オブジェクトのブロックスコープを無視してしまうのです。

const v = 「「2{」」
    w:「「5{」」
        x:()=>console.log(this)
    「「5}」」
「「2}」」  

v.w.x();
「「3▶︎ Window{....}」」

多重の入れ子にしたとしても、オブジェクトのスコープはことごとくすり抜け、グローバル空間にあるwindowオブジェクトを参照してしまいます💧




関数スコープの参照するthis。


@1@ 外側の関数スコープから見た「this」を参照する。

冒頭でも記した、この法則。
アロー関数のthisを制御するのは、オブジェクトのスコープではなく、関数スコープなのです。

const b = {
    B:()=>console.log(this)
};

先ほどwindowオブジェクトを参照していたアロー関数を、関数スコープの中で実行するように定義してみましょう。

const b = {
    B:function() {
        const func = ()=> console.log(this);
        func();
    }
};

メソッドBをフツーの関数スコープで定義。
その中でさっきのアロー関数を定数「func」として初期化、さらに実行させています。

b.B();
「「3▶︎ {B:f}」」

外部からメソッドBを実行したところ、オブジェクトbにアクセスできました。

const b = {
    B:「「1function() {」」
        const func = ()=> console.log(this);
        func();
    「「1}」」
};

このような結果が得られたのは、アロー関数を関数スコープの中に閉じ込めたからです。

@1@ アロー関数funcは、関数スコープであるfunction( ){....}の中にいます。
@2@ この関数スコープは、プロパティBそのもの。つまり、オブジェクトbのメソッドですね。
@3@「thisの基本法則」の記事でも述べたとおり、メソッドが参照するthisは、自身のオブジェクトでした。それにより、「console.log(this)」は、オブジェクトbを出力したのです。

アロー関数内のthisの立場から言うと「自身の外側にある関数スコープから見たthisを参照した」ことになります。
アロー関数のthisは、オブジェクトのスコープは無視。そのかわり、1つ外側にある関数スコープを元に、その対象をさがすのです。

@2@ 関数スコープの中にいなければ、windowオブジェクトを指す。

もう1つ、法則⬆︎がありました。
アロー関数を閉じ込める前のコードを見てみましょう。

const b = {
    B:()=> console.log(this)
};

b.B();
「「3▶︎ Window{....}」」

このアロー関数には「外側の関数スコープ」はありません。
「thisをさがす際の基準」となる関数スコープが見つからないため、1番外側にあるwindowオブジェクトを参照した、ということなんです。




メリット1、メソッド内関数。


アロー関数のthisが、外部の関数スコープを基準にするという性質。これにはどのようなメリット・使いどころがあるのでしょうか?

〜1つには、メソッド内で関数を実行する場合です。

const c = {
    num:10,
    func:function() {
        const C = function(){
            console.log(this.num);
        }
        C();
    }
};

オブジェクトcには、numプロパティとfuncメソッドがあります。
funcメソッドの中で「this.num」を出力する関数Cを定義し、実行するようにしています。
ここで期待するのはnumプロパティの値である「10」ではないでしょうか。

c.func();
「「3▶︎ undefined」」

残念ながらそうはなりません。funcメソッドを実行すると「undefined」となってしまいます。

  func:「「4function() {」」
        const C = function(){
            console.log(this.num);
        }
        C();
    「「4}」」

通常関数のthisは、それがメソッドである場合のみ、自身のオブジェクトを参照するのでした。
⬆︎のコードにおいて、外側にある紫のfunction( ) {....}は、確かにメソッドです。ですが、内側のconst Cの定義はfuncメソッド内の1処理にすぎません。つまり、メソッドではない、ということです。

メソッドではないゆえに、const Cのthisはwindowオブジェクトを参照します。
「windowオブジェクトのnumプロパティは定義されていません」ということで、undefinedとなってしまったのです。

funcメソッドの内部で、自身のオブジェクトであるcを参照したい。
それには、「外側の関数スコープからthisを探してくれる」アロー関数の出番です。

const Cを書き換えてみましょう。

const c = {
    num:10,
    func:「「4function() {」」
        const C = 「「1()=> console.log(this.num)」」
        C();
    「「4}」」
};

このようにすれば、thisは外側の関数スコープである紫部分からその対象を探してくれるようになります。
紫スコープはconst cのメソッドなので、thisの対象は自身のオブジェクトです。

c.func();
「「3▶︎ 10」」

無事、numプロパティにアクセスすることができました。
アロー関数のメリット1つ目は、「メソッド内の関数でもオブジェクト自身を参照できる」ところです。




メリット2、引数内関数。


メソッドのフツー関数にはもう1つ、問題があります。それは「引数内の関数ではwindowを参照してしまう」という点です。

const d = {
    func:function(){console.log(this)}
};

d.func();
「「3▶︎ {func:f}」」

オブジェクトdを作り、その中にfuncメソッドを定義しました。またもや!ですが、thisを出力する処理です。
これはメソッド直下のthisなので、自身のオブジェクトであるdにちゃんとアクセスしてくれます。

ところで。
このconsole.logの処理を時間差で行うとしたら、可能なのでしょうか。func2を作成し、setTimeoutの引数に取り入れてみます。

const d = {
    func:function(){console.log(this)},
    func2:function(){
        setTimeout(「「4this.func」」,2000);
    }
};

d.func2();
「「3▶︎ 2s後にwindow{....}」」

実行してみたところ、オブジェクトdではなくwindowオブジェクトが参照されました。 引数のところに、間接的にfuncを呼んだからでしょうか?

const d = {
    func3:function() {
        setTimeout(function(){
        console.log(「「4this」」);
        },2000);
    }
};

d.func3();
「「3▶︎ 2s後にwindow{....}」」

こんどは引数内に直接処理を書いてみます。が、やはり自身のオブジェクトを参照することができません。メソッドのthisであるにも関わらず、です。

このように、メソッドの引数にフツー関数を取り込むと、それに含まれるthisはグローバルオブジェクトを指してしまうのです。不便ですね。
でもご心配なく。ここでまたアロー関数が活躍します。引数部分の記述を書き換えてみましょう。

const d = {
    func4:function() {
        setTimeout(()=>console.log(「「1this」」),2000);
    }
};

前述のとおり、関数スコープは引数部分も含まれています。アロー関数のthisは、外側の関数スコープから見たthisを探すんでしたよね。
この関数スコープは、オブジェクトdのメソッドです。したがってthisが参照するのは、オブジェクトdということになります。

d.func4();
「「3▶︎ 2s後に{func4:f}」」

実行すると、オブジェクトdの内容がconsoleに出力されました。
アロー関数のメリット2つ目は、「メソッド引数内の関数でもオブジェクト自身を参照できる」ところです。




覚えておきましょう。


最後までお読み下さり、ありがとうございます。

オブジェクトの内容を利用して何か処理をしたい、という事は多々あるでしょう。ところがフツー関数を入れ子にすると、グローバルオブジェクトを指してしまいます。 そんなときはアロー関数を上手く利用しましょう。

アロー関数のthis。少し複雑な内容でしたね。ただこれを知っておくと、Javascriptのオブジェクト操作を行う際に非常に役に立ちます。困った時には思い出してみて下さい。
ではまた〜🎵






「ふ」です。

ベクターグラフィック、web、ガジェットなど。役立つ情報や観ていてたのしいページを書いていきたいと思います。