arguments と Function.apply(this) を上手に扱う
追記: apply("add string") で何が渡ってるのか見てみました。
Chrome以外のブラウザは new String("add string") が渡されてました
function func1(var_args) { func2.apply("add string", arguments); // this に "add string" そのものを渡す } function func2(var_args) { alert("typeof this = " + typeof this); if (this instanceof String) { alert("this instanceof String"); } alert(this); // -> "add string" }
typeof this | this instanceof String | |
Google Chrome4 | "string" | false |
Safari4 | "object" | true |
Firefox2 〜 3.5 | "object" | true |
IE8 | "object" | true |
Opera9.2 〜 10 | "object" | true |
あれ? (つд⊂)ゴシゴシ WebKit≒Chrome なのかな?
素の string だと…
var str = "string"; alert("typeof str = " + typeof str); if (str instanceof String) { alert("str instanceof String"); }
typeof str | str instanceof String | |
Google Chrome4 | "string" | false |
Safari4 | "string" | false |
Firefox2 〜 3.5 | "string" | false |
IE8 | "string" | false |
Opera9.2 〜 10 | "string" | false |
こうなりますが、new String("add string") だと
typeof this | this instanceof String | |
Google Chrome4 | "object" | true |
Safari4 | "object" | true |
Firefox2 〜 3.5 | "object" | true |
IE8 | "object" | true |
Opera9.2 〜 10 | "object" | true |
こうなりますね。Chrome 以外のブラウザでは、"add string" ではなく、new String("add string") なオブジェクトが渡っているようです。
os0x さんご指摘のように、toString() すると文字列が取れてくるけど、string から派生して string のように使えるオブジェクトが渡っていますね。jsライブラリの型判定関数(isString 等)や JSON.stringify に this を渡す場合は、素のオブジェクトに変換( this.toString() とか this + "" )してから渡すと安全ですね。
本文
JavaScript は arguments キーワードにより可変長引数が扱えます。
func.apply(this) や func.call(this) で func() 内での this を制御することもできます。
今日は「this はスコープを制御する以外にも使い道がある」ってことを書きます。
教科書に書かれてそうな arguments の使い方
以下は、func1() の引数の先頭に、文字列("add string")を追加し、関数func2を呼び出します。
function func1(var_args) { var arg = _ary(arguments); // 配列に変換 arg.unshift("add string"); // 先頭に挿入 func2.apply(this, arg); // 加工した配列を渡す } function func2(var_args) { var ary = _ary(arguments); // 配列に変換 var str = ary.shift(); // 先頭要素を切り出し alert(str); // -> "add string" } // arguments を配列に変換する function _ary(fake) { var rv = [], ri = -1, i = 0, iz = fake.length; for (; i < iz; ++i) { rv[++ri] = fake[i]; } return rv; } func1();
さっき気が付いた使い方
this が自由に使えるケースでは、すっきり書けます。
function func1(var_args) { func2.apply("add string", arguments); // this に "add string" そのものを渡す } function func2(var_args) { alert(this); // -> "add string" } func1();
this には、hash を渡したり, arguments の中身も渡せます。
# func2.call(arguments, "add string"); はうごきません(スコープの問題)
function func1(var_args) { func2.call(_ary(arguments), "add string"); // 配列化した引数を this に指定 } function func2(arg) { alert(this[0].key); // 1 alert(arg); // -> "add string" } function _ary(fake) { var rv = [], ri = -1, i = 0, iz = fake.length; for (; i < iz; ++i) { rv[++ri] = fake[i]; } return rv; } func1({ key: 1 }); // { key: 1 } を渡す
まとめ
引数は this でも渡せます。this も使ってスッキリ書きましょう。
追記
関数内で this は上書できないので、普通の引数とはちょっと違います。
this = "hoge"; // NG
お約束
js の this とスコープをちゃんと理解していない方にはお勧めできません。