暗黙的な関数パラメータの使用
関数には、暗黙的に渡されるパラメータが2つある。arguments
とthis
である。
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
はイベントが発生したオブジェクトを指す。このように、関数のコンテキストを呼び出し側から操作する方法がapply
とcall
である。
関数の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
メソッドは、別の関数コンテキストを持った別の関数を返す関数である。