NodeListを配列に変換するのにArray.applyを使うのはどうか

NodeListHTMLCollectionを配列(Array)に変換する方法の一つに、Array.prototype.sliceを使う方法がある。

var array = Array.prototype.slice.call(document.getElementsByTagName('A'));

でも、最近の実装であればArray.applyもその用途に使える。

var array = Array.apply(null, document.getElementsByTagName('A'));

ES3ではFunction.prototype.applyの第二引数には配列(Array)かargumentsしか取れなかったけど、ES5でその制限が緩和され、NodeListなどの array like オブジェクトも渡せるようになった。

なお、IE8以下ではどちらの方法でもエラーになるので注意(エラーメッセージは「JScript オブジェクトを指定してください」)。ループで処理するしかない。

var array = [];
var nodes = document.getElementsByTagName('A');
for (var i = 0, l = nodes.length; i < l; ++i) {
    array[i] = nodes[i];
}

ちなみに、argumentsの配列化にArray.applyは使えない。argumentsは要素が単一の数値だけになる可能性があり、その場合意図しない結果になる。

(function () {
    var args = Array.apply(null, arguments); // Array(5, 6, 7) つまり [5, 6, 7]
    console.log(args.shift()); // 5
})(5, 6, 7);

(function () {
    var args = Array.apply(null, arguments); // Array(5) つまり length: 5 の配列
    console.log(args.shift()); // undefined
})(5);

lengthが1でないことを確かめてからなら使える(参考:slice vs [].apply · jsPerf)けど、Array.prototype.sliceを使う方がシンプル。

(function () {
    var args = arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments);
    console.log(args.shift()); // 5
})(5);

(function () {
    var args = Array.prototype.slice.call(arguments);
    console.log(args.shift()); // 5
})(5);

おまけ:ES3実装およびIE8以下でFunction.prototype.applyをES5仕様にするモンキーパッチ。