松山事務所の石丸です。
あるところにログ種別、フォーマット文字列、可変長のパラメータを受け取る みんなの大好きな printf
のようなログ出力関数がありました。
それをそのまま使ってもいいのですが、ログ種別 “warning” を何度も間違えずに書く自信がないおじさんは、ログ種別毎に関数を用意しました。
関数名にしてしまえばIDEの支援が受けられて補完が効くし、参照も拾ってくれますよね。
1 2 3 4 5 6 7 8 9 10 11 |
function writeLog(type, format, var_args) { // フォーマットしてログ出力 } function warningLog(format, var_args) { writeLog("warning", format, var_args); } function errorLog(format, var_args) { writeLog("error", format, var_args); } |
いまにも動き出しそうですが、最後の引数 var_args は名前の雰囲気が可変長なだけで、ただの1つの引数でしかありません。
どうすればerrorLog関数やwarningLog関数からwriteLog関数へ可変長引数を伝えることができるのでしょうか?
そもそもどうやって可変長引数にアクセスするのか
可変長引数にアクセスするためには arguments
を使います。
arguments は、関数へ渡された引数に対応する Array のようなオブジェクトです。
Arrayのようなオブジェクト arguments
に配列のようにアクセスしてみます。
1 2 3 4 5 6 7 |
function errorLog(format, var_args) { alert(arguments[0]); // %s %d %s alert(arguments[1]); // arg1 alert(arguments[2]); // 2 } errorLog("%s %d %s", "arg1", 2, "arg3"); |
arguments
には呼び出し元から渡されたパラメータがすべて入っているので、第1引数のフォーマット文字列まで取れてしまいます。
もちろん普段と同じように format や var_args でも arguments[0]、arguments[1]と同じ値が参照できます。
どうやってargumentsを渡すのか
可変長引数にアクセスする方法はわかりましたが、errorLog関数からwriteLog関数へどのように可変長引数を渡すのか。
1 2 3 |
function errorLog(format, var_args) { writeLog("error", arguments); } |
と書いてしまいそうですが、arguments
はただの配列のようなオブジェクトでしかないので、
この書き方では”error”と arguments
2つのパラメータでwriteLog関数を呼び出しただけになってしまいます。
writeLog関数の中で arguments
を見てみると次のような形で渡されていました。
1 2 3 4 5 6 7 8 9 |
{ "0": "error", "1": { "0": "%s %d %s", "1": "arg1", "2": 2, "3": "arg3" } } |
次のようなフラットな形で渡したいですよね。
1 2 3 4 5 6 7 |
{ "0": "error", "1": "%s %d %s", "2": "arg1", "3": 2, "4": "arg3" } |
配列のようなオブジェクトをパラメータに関数を呼び出すにはFanction.applyを使用します。
Function.prototype.apply() – JavaScript | MDN
arguments
をただ引き渡すだけなら
1 2 3 |
function errorLog(format, var_args) { writeLog.apply(this, arguments); } |
でいいのですが、今回はログ種別も渡したい。
でもapplyは thisArg
と argsArray
2つのパラメータしか受け取ってくれません。
最終的には次のようになりました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function writeLog(type, format, var_args) { // フォーマットしてログ出力 } function warningLog(format, var_args) { Array.prototype.unshift.call(arguments, "warning"); writeLog.apply(this, arguments); } function errorLog(format, var_args) { Array.prototype.unshift.call(arguments, "error"); writeLog.apply(this, arguments); } errorLog("%s %d %s", "arg1", 2, "arg3"); |
arguments
の先頭にログ種別を差し込んでapplyです。
use strict だと arguments
の上書きが出来ないらしいので、argumentsをsliceしてからunshiftですかね。
もっとエレガントでクールな方法があったら教えてください。ガムあげます。
追記
ES2015以降ならスプレッド構文でクールにかけるぜ!
とイケメンからアドバイスをいただきました。
1 2 3 |
function errorLog(format, var_args) { writeLog(format, 'error', ...arguments); } |
良いですね!ガムあげます。