FirefoxにおけるArray#concatは strict mode function 相当

Array.prototypeの大半のメソッドは、配列以外のオブジェクト(NodeListなどの擬似配列など)にも転用できるように汎用的に定義されている。Array.prototype.concatもその一つだけど、他のメソッドとは異なりthis値に配列っぽさは求められておらず、どんなオブジェクトでもよい。this値は引数と同じように処理される。

Array.prototype.concat.call(1, 2, 3, 4, 5); //=> [1, 2, 3, 4, 5] // ?
Array.prototype.concat.call("A", "B", "C"); //=> ["A", "B", "C"] // ?

ただし、通常の関数 (non-strict mode function) ではthis値は必ずオブジェクトになる。数値はNumberオブジェクトに、文字列はStringオブジェクトにラップされてしまう。? と書いたのはそのこと。Firefox以外のブラウザでは次のような結果になる。

var array = Array.prototype.concat.call(1, 2, 3, 4, 5);
typeof array[0]; //=> "object"
typeof array[1]; //=> "number"

Firefoxではtypeof array[0];の戻り値は"number"になる。this値はラップされない。strict mode function として定義されているのだろうか。

If this is evaluated within strict mode code, then the this value is not coerced to an object.

Annotated ES5
function nonStrictFn() {
    return this;
}
function strictFn() {
    "use strict";
    return this;
}
typeof nonStrictFn.call(9); //=> "object"
typeof strictFn.call(9);    //=> "number"

Generic method

FirefoxではString.prototypeArray.prototypeのメソッドを転用しやすいように Generic method が用意されている。例えば、Array.prototype.concat.callの代わりにArray.concatと書ける。

Array.concat(1, 2, 3, 4, 5); //=> [1, 2, 3, 4, 5]

このとき、戻り値の一つ目の要素がオブジェクトにラップされていたら混乱を招くと思う。これがArray.prototype.concatの実装に影響を与えているんだと思う。

以前書いたFirefox以外の実装にも Generic method を定義するためのスクリプトFirefoxの実装に合わせることにした。もちろん、Array.prototype.concatの方は弄らない。

転用の必要性

そもそも、Array.prototype.concatは転用する必要がないように思える。[].concat(item1, item2, ...)で事足りる。

var array;
array = [].concat(1, 2, 3, 4, 5); //=> [1, 2, 3, 4, 5]
typeof array[0]; //=> "number"

Array.prototype自体も配列 (Array.isArray(Array.prototype) === true) なので、Array.prototype.lengthが弄られていないという前提の下、次のようにも書ける。

// Function#callを使わない
Array.prototype.concat(1, 2, 3, 4, 5); //=> [1, 2, 3, 4, 5]