Secrets of the JavaScript Ninja 2nd 読書メモ 第4章 関数呼び出しを理解する

Secrets of the Javascript Ninja

暗黙的な関数パラメータの使用

関数には、暗黙的に渡されるパラメータが2つある。argumentsthisである。

argumentsパラメータ

argumentsパラメータは関数への引数の集合である。argumentsを使えば明示的に定義されていないパラメータも受け取ることができる。これを利用すると、関数オーバーロードを実現できる。

ES2015のrestパラメータを使用すれば、argumentsが必要になる場面は減少するが、古いコードに触るときのためにもargumentsの挙動は理解しておいたほうがよい。
argumentsにはlengthプロパティがあり、引数の数を調べることができる。また、argumentsからは配列アクセスの形式で値を取り出せる。

function whatever() {
  for (let i = 0, len = arguments.length; i < len; i += 1) {
    console.log(arguments[i]);
  }
}

whatever();
whatever(1);
whatever(1, 2);

argumentsは配列のようだが、配列ではない。そのため、sort等の配列操作用メソッドを使うことはできない。配列のように扱いたい場合は、ES2015のArray.fromメソッド等を使用して配列に変換する必要がある。

function sum() {
  return Array.from(arguments).reduce((p, c) => p + c, 0);
}

console.log(sum()); // 0
console.log(sum(1)); // 1
console.log(sum(2, 3)); // 5

// なお、restパラメータは配列なので変換不要
function sum (...args) {
  return args.reduce((p, c) => p + c, 0);
}

thisパラメータ

thisは関数呼び出しのコンテキストを保持するパラメータである。JavaやC#のようなオブジェクト指向言語とは異なり、JavaScriptのthisの値はどのように呼び出されたかによって異なる。JavaScriptのオブジェクト指向の理解にあたっては、thisは最も重要な部分のひとつである。

関数呼び出し

関数呼び出しにはいくつかのパターンがある。

  • 通常の関数: skulk()
  • メソッド: ninja.skulk()
  • コンストラクタ: new Ninja()
  • applyまたはcallによる呼び出し: skulk.call(ninja) または skulk.apply(ninja)

関数としての呼び出し

関数としての呼び出しでは、thisの指す値はstrictモードか否かによって異なる。

function ninja() {
  return this; // globalオブジェクト(ブラウザならwindow)
}

function samurai() {
  "use strict";
  return this; // undefined
}

メソッドとしての呼び出し

関数がオブジェクトのプロパティとして割り当てられている場合、この関数をオブジェクトのメソッドと呼ぶ。この場合、thisパラメータが指すのは、メソッド呼び出しの対象となったオブジェクトである。

var ninja = {};
ninja.getThis = function(){ return this; };
console.log(ninja.getThis() === ninja); // true

コンストラクタとしての呼び出し

コンストラクタとは、オブジェクトを組み立てる関数である。コンストラクタを使うことで、同様の機能を持つオブジェクトを簡単に作ることができる。

function Ninja() {
  this.skulk = function() {
    return this;
  }
}

var ninja1 = new Ninja();
var ninja2 = new Ninja();

console.log(ninja1.skulk() === ninja1);
console.log(ninja2.skulk() === ninja2);

関数がnew演算子とともに呼び出されると(コンストラクタとしての呼び出し)、空のオブジェクトがthisに割り当てられる。このオブジェクトはnew演算子の結果として返る。

applyとcallによる呼び出し

イベントハンドラに紐付けた関数において、thisはイベントが発生したオブジェクトを指す。このように、関数のコンテキストを呼び出し側から操作する方法がapplycallである。

関数のapplyメソッドを使って呼び出しを行う場合、第1引数に関数コンテキストとして用いるオブジェクト、第2引数に呼び出し時の引数の配列を渡す。callメソッドも同様だが、引数は配列ではなくパラメータリストに直接渡す。

function juggle() {
    let result = 0;
    for (let n = 0; n < arguments.length; n += 1) {
        result += arguments[n];
    }
    this.result = result;
}

const ninja1 = {};
const ninja2 = {};

juggle.apply(ninja1, [1, 2, 3, 4]);
juggle.call(ninja2, 5, 6, 7, 8);

console.log(ninja1.result); // 10
console.log(ninja2.result); // 26

関数コンテキストの問題を修正する

関数コンテキストを操作する方法には、call/applyの他にアロー関数式とbindメソッドがある。

アロー関数式による関数コンテキストの回避

アロー関数式は、みずからのthisをもたない。代わりに、その関数が宣言された時点のthisパラメータの値を保持している。
イベントハンドラとして関数を登録しても、thisの値は変わらない。

bindメソッドの使用

関数のbindメソッドを使用すると、その関数が実行されるコンテキストを呼び出し方法に関わらず変更できる。bindメソッドは、別の関数コンテキストを持った別の関数を返す関数である。

コメントを残す

コメントを残す