JavaScript 関数呼び出し

JavaScript 関数呼び出し: 呼び出し方式、引数渡し、'this'の参照先を詳しく解説

このアーティクルでは、JavaScript における関数呼び出しの仕組みについて掘り下げ、呼び出し方式、引数の受け渡しメカニズム、そして状況によって変化する 'this' キーワードの参照先について解説します。関数呼び出しを深く理解することで、より洗練された効率的な JavaScript コードの記述が可能になります。

副題

1. 関数の呼び出し方4パターン

JavaScript では、関数を呼び出す方法がいくつか存在し、呼び出し方によって 'this' の参照先が異なります。主な呼び出し方は以下の 4 つです。

呼び出し方 構文 'this' の参照先
関数として呼び出す 関数名(引数1, 引数2, ...) myFunction(1, 2, 3); グローバルオブジェクト (ブラウザ環境では window、Node.js 環境では global)
メソッドとして呼び出す オブジェクト.メソッド名(引数1, 引数2, ...) myObject.myMethod(); 呼び出し元のオブジェクト (myObject)
コンストラクタとして呼び出す new 関数名(引数1, 引数2, ...) let myInstance = new MyConstructor(); 新しく生成されたオブジェクトインスタンス
call() / apply() メソッドを使用する
  • call(thisArg, 引数1, 引数2, ...)
  • apply(thisArg, [引数1, 引数2, ...])
  • myFunction.call(myObject, 1, 2, 3);
  • myFunction.apply(myObject, [1, 2, 3]);
thisArg で指定したオブジェクト

  <script>
    // 関数として呼び出す例
    function greet() {
      console.log('こんにちは、' + this);
    }
    greet(); // 'this' はグローバルオブジェクト (window) を参照

    // メソッドとして呼び出す例
    const person = {
      name: '太郎',
      sayHi: function() {
        console.log('こんにちは、' + this.name + 'です。');
      },
    };
    person.sayHi(); // 'this' は person オブジェクトを参照

    // コンストラクタとして呼び出す例
    function Car(brand) {
      this.brand = brand;
    }
    const myCar = new Car('トヨタ');
    console.log(myCar.brand); // 'this' は新しく生成された myCar オブジェクトを参照

    // call() / apply() を使用した呼び出し例
    function introduce(message) {
      console.log(message + '、私は' + this.name + 'です。');
    }
    introduce.call(person, '初めまして'); // 'this' は person オブジェクトを参照
    introduce.apply(person, ['こんにちは']); // 'this' は person オブジェクトを参照
  </script>
  

2. 関数の引数渡し

JavaScript では、関数の引数は**値渡し**で渡されます。基本型の場合、関数に渡されるのは値のコピーであるため、関数内部で値を変更しても、元の変数の値には影響を与えません。


  <script>
    function changeValue(x) {
      x = 100;
      console.log('関数内部: ', x); // 100
    }

    let num = 50;
    changeValue(num); 
    console.log('関数外部: ', num); // 50 (値は変わらない)
  </script>
  

一方、オブジェクトなどの参照型の場合、関数に渡されるのは参照値のコピーです。つまり、関数内部からオブジェクトのプロパティを変更すると、元のオブジェクトにも影響が及びます。


  <script>
    function changeObject(obj) {
      obj.name = '花子';
      console.log('関数内部: ', obj.name); // 花子
    }

    const person = { name: '太郎' };
    changeObject(person);
    console.log('関数外部: ', person.name); // 花子 (オブジェクトのプロパティが変更される)
  </script>
  

3. 'this' キーワードの参照先

前述の通り、'this' キーワードの参照先は関数呼び出し方法によって異なります。主なパターンは以下の通りです。

呼び出し方 'this' の参照先
関数として呼び出す グローバルオブジェクト (ブラウザ環境では window、Node.js 環境では global)
メソッドとして呼び出す 呼び出し元のオブジェクト
コンストラクタとして呼び出す 新しく生成されたオブジェクトインスタンス
call() / apply() メソッドを使用する call() / apply() の第一引数で指定したオブジェクト
アロー関数内 アロー関数を定義したスコープの 'this' を継承

4. 関数呼び出しにおける注意点とテクニック

  • 'this' の参照先を意識してコーディングを行い、予期せぬバグを防ぎましょう。特に、イベントリスナーやコールバック関数内では 'this' の参照先が変わることがあるので注意が必要です。
  • call() / apply() メソッドを有効活用することで、'this' の参照先を動的に変更し、コードの再利用性を高めることができます。
  • クロージャを理解し、関数実行時のコンテキストを保持するテクニックを習得しましょう。クロージャは、イベント処理や非同期処理などで効果を発揮します。

参考資料

関数呼び出しに関する Q&A

Q1: アロー関数における 'this' の挙動について教えてください。

A1: アロー関数は、従来の関数とは異なり、独自の 'this' を持ちません。代わりに、アロー関数を定義したスコープの 'this' を継承します。そのため、アロー関数は、オブジェクトのメソッドとして使用する場合や、'this' の参照先を動的に変更する必要がある場合に適していません。

Q2: 'call()' と 'apply()' の使い分け方を教えてください。

A2: 'call()' と 'apply()' はどちらも、関数の 'this' を明示的に設定するために使用されます。主な違いは、引数の渡し方です。'call()' は引数を個別に指定するのに対し、'apply()' は引数を配列として渡します。引数の数が事前に決まっていない場合や、配列として引数を管理している場合は 'apply()' を使用すると便利です。

Q3: 関数呼び出しにおけるパフォーマンスについて教えてください。

A3: JavaScript エンジンは年々進化しており、関数呼び出しのパフォーマンスは最適化されています。しかし、大量の関数呼び出しが発生する場合は、パフォーマンスへの影響を考慮する必要があります。特に、ループ内で繰り返し関数呼び出しを行う場合は、インライン化やキャッシュなどのテクニックを検討することで、パフォーマンスを向上させることができます。ただし、過度なパフォーマンス最適化はコードの可読性を損なう可能性もあるため、バランスが重要です。