WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

JavaScript の関数内での this

by WebSurfer 2023年11月6日 17:41

JavaScript の関数内での this の使い方を備忘録として残しておきます。

MDN ドキュメント「this」に JavaScript の this についていろいろ書いてあります。関数内での this については「関数コンテキスト」のセクションに書いてあるように "関数の呼び出し方によって異なります" ということです。ただし、アロー関数の場合はそれは当てはまらないようです。

というわけで、関数内で this を使う場合は、常に「その this は何なのか?」ということを考えてコードを書く必要がありそうです。

どういうことか具体例を以下に書きます。

下の画像は、Visual Studio 2022 のテンプレートを使って作成した React + Web API のプロジェクトを実行し、React 側から fetch API を使って Web API から天気予報データを取得してブラウザに表示したものです。

天気予報データを取得しブラウザに表示

Visual Studio が自動生成した React 側の JavaScript のコードは以下の通りです。React の componentDidMount のタイミングでコードの下の方にある populateWeatherData メソッドが呼び出されると、fetch API を使って Web API から天気予報データを取得し、取得したデータを this.setState メソッドを使って state に設定しています。

import React, { Component } from 'react';

export class FetchData extends Component {
    static displayName = FetchData.name;

    constructor(props) {
        super(props);
        this.state = { forecasts: [], loading: true };
    }

    componentDidMount() {
        this.populateWeatherData();
    }

    static renderForecastsTable(forecasts) {
        return (
            <table className="table table-striped" aria-labelledby="tableLabel">
                <thead>
                    <tr>
                        <th>Date</th>
                        <th>Temp. (C)</th>
                        <th>Temp. (F)</th>
                        <th>Summary</th>
                    </tr>
                </thead>
                <tbody>
                    {forecasts.map(forecast =>
                        <tr key={forecast.date}>
                            <td>{forecast.date}</td>
                            <td>{forecast.temperatureC}</td>
                            <td>{forecast.temperatureF}</td>
                            <td>{forecast.summary}</td>
                        </tr>
                    )}
                </tbody>
            </table>
        );
    }

    render() {
        let contents = this.state.loading
            ? <p><em>Loading...</em></p>
            : FetchData.renderForecastsTable(this.state.forecasts);

        return (
            <div>
                <h1 id="tableLabel">Weather forecast</h1>
                <p>This component demonstrates fetching data from the server.</p>
                {contents}
            </div>
        );
    }

    async populateWeatherData() {
        const response = await fetch('weatherforecast');
        const data = await response.json();
        this.setState({ forecasts: data, loading: false });
    }
}

上のコードの一番最後の行の this.setState メソッドの this が、関数の書き方によってどう変わってくるかを以下に書きます。

上のコード例で populateWeatherData メソッドは FetchData クラスのメソッドなので、MDN のドキュメント this の Class context のセクションに "the this value is the object that the method was accessed on." と書いてあるように、this には FetchData オブジェクトが設定されます。

this は FetchData オブジェクト

結果、Web API から取得したデータは this.setSate メソッドによって FetchData オブジェクトの state に設定され、この記事の一番上の画像のように天気予報データがブラウザに表示されます。

では、以下のように async/await は使わないで then() の中でコールバックを呼ぶように変更し、コールバックを function () { ... } という形にして、その function の中で this を使うとどうなるでしょう?

this は undefined

上の画像の通り this は undefined となります。結果、Web API から取得したデータの state への設定は失敗しますので、初期画面で Loading... と表示された状態で止まってしまいます。

MDN のドキュメント「関数コンテキスト」に書いてありますが、(1) strict モードでは、実行コンテキストに入るときに this 値が設定されていないと、undefined のままになり、(2) strict モードでない場合は、this は既定でグローバルオブジェクトすなわち window となるそうです。then() の中で非同期に実行される場合は「実行コンテキストに入るときに this 値が設定されていない」ということになるようで、上の画像の例では (1) という結果になっています。

次に、上の画像の例のコールバック function () { ... } を以下のようにアロー関数に代えて、その中で this を使うとどうなるでしょうか?

this は FetchData オブジェクト

上の画像の通り this は FetchData オブジェクトとなります。結果、Web API から取得したデータは FetchData オブジェクトの state に設定され、期待通り天気予報データがブラウザに表示されます。

MDN のドキュメント「アロー関数」によると "アロー関数では、this はそれを囲む構文上のコンテキストの this の値が設定されます" ということで、this には FetchData オブジェクトが設定されたということのようです。

アロー関数というのは従来の function() { ... } の簡潔な代替構文に過ぎないと思っていたのですが、MDN のドキュメント「アロー関数式」に書いてあるように意図的な使用上の制限があるそうです。その中の一つが this で、アロー関数自身では this の結び付けを行わず、包含する構文上のコンテキストの this の値を保持するのだそうです。

Tags: , , ,

JavaScript

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar