JavaScriptのthisについて

JavaScriptの学習において、最も理解が難しい概念の一つとして知られるのが this キーワードです。

this は関数がどのように呼び出されたかに依存して、その参照先が変わる特性を持っています。

この柔軟性がある意味で混乱を招き、多くの初心者にとって大きな壁となります。

しかし、this の動作を正しく理解することは、JavaScriptを深く理解し、効果的にコーディングするために不可欠です。

本章では、まず this キーワードの基本概念と定義を明らかにし、その後、さまざまなシチュエーションにおける this の挙動を詳細に解説します。

実践的なコードサンプルを通じて、this の理解を深め、複雑な問題にも対処できるスキルを養っていきましょう。

this とは何か

this キーワードはJavaScriptにおける特殊な識別子であり、関数やメソッドが呼び出された際に、その関数やメソッドが属するオブジェクトを参照します。

簡単に言えば、this は関数やメソッドが実行されている「文脈」を指し示すものです。

しかし、その値がどのオブジェクトを指すかは、関数やメソッドがどのように呼び出されたかによって変わります。

基本例 1: シンプルなオブジェクトの例

まず、シンプルなオブジェクトを使って this の動作を見てみましょう。

const person = {
    name: 'Alice',
    greet: function() {
        console.log(this.name);
    }
};
person.greet(); // 'Alice' が出力される
  • person オブジェクトには name プロパティと greet メソッドがあります。
  • greet メソッド内の thisperson オブジェクトを指します。
  • person.greet() が呼び出されると、this.nameperson.name を参照するため、'Alice' が出力されます。

基本例 2: 車オブジェクトの例

次に、car オブジェクトを使った例を見てみましょう。

この例では、複数のメソッドを持つオブジェクト内で this をどのように使うかを示します。

const car = {
    brand: 'Toyota',
    model: 'Corolla',
    start: function() {
        console.log(`${this.brand} ${this.model} is starting...`);
    },
    drive: function() {
        console.log(`${this.brand} ${this.model} is driving...`);
    }
};

car.start(); // 'Toyota Corolla is starting...' が出力される
car.drive(); // 'Toyota Corolla is driving...' が出力される
  • オブジェクトの定義:
    • car オブジェクトには brand プロパティと model プロパティ、そして startdrive というメソッドがあります。
  • メソッド内の this:
    • start メソッド内の thiscar オブジェクトを指します。
    • drive メソッド内の this も同様に car オブジェクトを指します。
  • メソッドの呼び出し:
    • car.start() を呼び出すと、this.brandcar.brand を参照し、this.modelcar.model を参照するため、'Toyota Corolla is starting...' が出力されます。
    • car.drive() を呼び出すと、同様に this.brandthis.modelcar オブジェクト内のプロパティを参照し、'Toyota Corolla is driving...' が出力されます。

この例では、car オブジェクトのメソッドが this を使ってオブジェクト自身のプロパティにアクセスすることで、動的にメッセージを生成しています。

このように、this はメソッド内でオブジェクトのプロパティや他のメソッドにアクセスするための強力な手段です。

this を使用しない場合のコード例

同じ car オブジェクトの例を、this を使用せずに書いてみます。

this を使用しない場合、各メソッドはオブジェクトのプロパティに直接アクセスする必要があります。

const car = {
    brand: 'Toyota',
    model: 'Corolla',
    start: function(carObject) {
        console.log(`${carObject.brand} ${carObject.model} is starting...`);
    },
    drive: function(carObject) {
        console.log(`${carObject.brand} ${carObject.model} is driving...`);
    }
};

car.start(car); // 'Toyota Corolla is starting...' が出力される
car.drive(car); // 'Toyota Corolla is driving...' が出力される
  • オブジェクトの定義:
    • car オブジェクトには brand プロパティと model プロパティ、そして startdrive というメソッドがあります。
  • メソッド内の引数:
    • this を使用しない場合、各メソッドはオブジェクト自身を引数として受け取る必要があります。
    • start メソッドと drive メソッドは、引数 carObject を使って brandmodel にアクセスします。
  • メソッドの呼び出し:
    • car.start(car) を呼び出すと、引数として渡された car オブジェクトのプロパティを使ってメッセージを生成します。
    • 同様に、car.drive(car) も引数として渡された car オブジェクトのプロパティを使ってメッセージを生成します。

this を使用しない場合、各メソッドでオブジェクト自身を引数として受け取る必要があります。

これにより、コードが冗長になり、メソッドの再利用性が低下します。

this を使用することで、オブジェクト内のメソッドは自動的にそのオブジェクトのプロパティにアクセスできるため、コードが簡潔で可読性が高くなります。

基本例 3: コンストラクタ内の例

コンストラクタ関数は、新しいオブジェクトを作成し、そのオブジェクトを初期化するために使用されます。

コンストラクタ関数内では、this キーワードは新しく作成されたオブジェクトを参照します。

これにより、コンストラクタ内で新しいオブジェクトのプロパティを設定することができます。

function Car(brand, model) {
    this.brand = brand;
    this.model = model;
    this.start = function() {
        console.log(`${this.brand} ${this.model} is starting...`);
    };
    this.drive = function() {
        console.log(`${this.brand} ${this.model} is driving...`);
    };
}

const myCar = new Car('Toyota', 'Corolla');
console.log(myCar.brand); // 'Toyota'
console.log(myCar.model); // 'Corolla'
myCar.start(); // 'Toyota Corolla is starting...' が出力される
myCar.drive(); // 'Toyota Corolla is driving...' が出力される
  • コンストラクタ関数の定義:
    • Car という名前のコンストラクタ関数を定義しています。この関数は brandmodel の2つの引数を取ります。
    • コンストラクタ関数内で、this.brandthis.model が新しく作成されるオブジェクトのプロパティとして設定されます。
  • メソッドの定義:
    • start メソッドと drive メソッドも同様に this を使って定義されます。これにより、これらのメソッドは新しく作成されたオブジェクトのプロパティとして使用できます。
  • 新しいオブジェクトの作成:
    • new キーワードを使って、Car コンストラクタ関数を呼び出すことで、新しい Car オブジェクトを作成します。
    • const myCar = new Car('Toyota', 'Corolla'); のように呼び出すと、新しい Car オブジェクトが生成され、そのオブジェクトの brandmodel プロパティが設定されます。

詳細な動作

  1. コンストラクタ呼び出し:
    • new Car('Toyota', 'Corolla') が呼び出されると、Car 関数内の this は新しく作成された空のオブジェクトを指します。
    • this.brandthis.model が設定され、新しいオブジェクトにプロパティが追加されます。
  2. メソッドの設定:
    • this.startthis.drive にメソッドが追加され、これらのメソッドも新しく作成されたオブジェクトのプロパティになります。
  3. オブジェクトの使用:
    • myCar オブジェクトの brandmodel をコンソールに出力すると、それぞれ ‘Toyota’ と ‘Corolla’ が表示されます。
    • myCar.start()myCar.drive() を呼び出すと、それぞれ 'Toyota Corolla is starting...''Toyota Corolla is driving...' が表示されます。

コンストラクタ内での this は、新しく作成されたオブジェクトを参照します。

これにより、コンストラクタ関数内で新しいオブジェクトのプロパティやメソッドを設定することができます。

this を使用することで、同じコンストラクタ関数を使って複数のオブジェクトを作成し、それぞれに独自のプロパティやメソッドを持たせることができます。

基本例 4: アロー関数の例

Arrow関数(アロー関数)は、ES6(ECMAScript 2015)で導入された新しい関数の書き方です。

通常の関数と異なり、Arrow関数は自分自身の this コンテキストを持ちません。

代わりに、定義されたスコープ(親スコープ)の this を継承します。

これにより、特定の状況での this の挙動を理解しやすくし、バインディングの問題を解決することができます。

まずは通常の関数について見てみましょう。

通常の関数(関数宣言や関数式)は、その関数がどのように呼び出されたかに応じて this が変わります。

関数がメソッドとして呼び出されると、そのメソッドを所有するオブジェクトが this になります。

const person = {
    name: 'Alice',
    greet: function() {
        console.log(this.name); // `this` は `person` オブジェクトを指す
    }
};
person.greet(); // 'Alice' が出力される

この例では、greet メソッド内の thisperson オブジェクトを指しています。

次にアロー関数を見てみましょう。

Arrow関数は通常の関数とは異なり、自分自身の this を持たず、定義されたスコープの this を継承します。

そもそもアロー関数では引数を持たないため、自分自身のthisを持つことはありません。

そして、この特性がクロージャとしての役割を果たしており、アロー関数はクロージャの一部として動作します。

const person = {
    name: 'Alice',
    greet: function() {
        const innerFunction = () => {
            console.log(this.name); // `this` は外側のスコープ(`greet` メソッド)の `this` を継承
        };
        innerFunction(); // 'Alice' が出力される
    }
};
person.greet();

この例では、innerFunction がArrow関数として定義されており、外側のスコープである greet メソッドの this を継承しています。

以下のように書いても同じことが言えます。

const person = {
    name: 'Alice',
    greet() {
        const innerFunction = () => {
            console.log(this.name);
        };
        innerFunction(); // 'Alice' が出力される
    }
};
person.greet();

この例ではgreet メソッドはオブジェクトのプロパティとして定義されているため、変数宣言 (constlet など) は不要です。

また、今回の例ではいずれもgreet メソッドに const を指定する必要はありません。

例として説明するために const を使用していますが、実際には greet メソッドはオブジェクトのプロパティとして定義されるので、const などの変数宣言は不要です。

まとめ

thisについて理解することができたでしょうか?

一度読んだだけで理解することはなかなか難しいと思いますが、使っているうちになんとなくわかるようになってくることもあります。

特にオブジェクト化をしっかりと理解することができれば、コンストラクタの例がはっきりとわかるようになると思います。(私はオブジェクト化を理解してからthisを理解できました。)

JavaScriptを学ぶ上で避けて通ることができないthisですが、覚えると非常に便利ですので、頑張って使い方を覚えていきましょう。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です