ここにおける JavaScript とは Mozilla 界隈におけるそれのことである。
しばらく JavaScript から離れており、JavaScript 1.8 あたりからの新機能がわからなかったので、MDN を見ながらがんばってみた。
今回はなぜか Array
オブジェクトに zip()
メソッドを追加する必要があったので、Ruby の Array#zip
の挙動を参考に作った。一般に、同様のメソッドなり関数なりは複数の配列の同一インデックスの要素を配列にし、それらの配列を要素とする配列を返すような挙動であるが、それぞれ長さが異なる配列を扱う際に、返す配列の長さを呼び出し元の配列の長さに合わせる挙動という意味で Ruby に合わせた。結果として不足分は undefined
が入り、余った分は切り捨てられる。実行結果としては次のようになる。
[1, 2, 3].zip([4, 5], [6, 7, 8, 9]); // => [[1, 4, 6], [2, 5, 7], [3, undefined, 8]]
これを従来の JavaScript 1.6 で実装すると次のようになる。
Array.prototype.zip = function Array_zip(){ var args = Array.slice(arguments); return this.map(function(e, i){ return this.map(function(a){ return a[i]; }); }, [this].concat(args)); };
可変個引数を扱うために arguments
オブジェクトをごにょごにょし、Array#map()
の第 2 引数ではコールバック関数内で this
となるオブジェクトを渡したりしている。
さらに新しめの Firefox で動く JS ではこうなる。
Array.prototype.zip = function Array_zip(...args) this.map((e, i) => [this, ...args].map(a => a[i]));
これはまず、代入する関数が式クロージャなるものになっている。これは Firefox 3 で導入された JavaScript 1.8 の新機能で、return
文と波括弧を省略できるというものである。
さらに、この関数の仮引数で ...args
となっているのは Firefox 15 で導入された rest パラメータなるものである。従来は配列のような arguments
オブジェクトを使ったが、ここで得られる args
は配列であるため、Array.slice(arguments)
あるいは Array.prototype.slice.call(arguments)
のような小細工なしに引数リストを配列として得られる。ただし、function f(a, b, ...c){}
のような場合で得られる c
は a
と b
が含まれないので arguments
との使い分けが求められる。
また、Array#map()
の引数として渡している関数は Firefox 22 で導入されたアロー関数なるものである。これは無名関数式の表記法の 1 つであるのだが、従来品との違いは、関数内で this
の指す物が異なる、コンストラクタ関数になれない、ということらしい。
今回重要なのは this
であって、誤解を恐れずに書けば、コールバック関数内で this
は外側における this
と同じ物のを指しているため、あらかじめ外側で var self = this;
としてコールバック内で self
を参照するという必要がない。従来品の例では [this].concat(args)
という Array
オブジェクトを Array#map()
の第 2 引数で渡し、コールバック関数内での this
としているが、var self = this;
しておき、コールバック関数内で [self].concat(args).map( ... )
とすれば同じである。この場合は Array#map()
の第 2 引数は不要である。
従って、アロー関数を使えば、この部分を [this].concat(args).map( ... )
とすることができる。さらに、新しめ版では Firefox 16 で導入された、配列リテラルの展開演算子を使っている。この演算子は [1, 2, ...array, 3]
のように書くと、array
部分が展開された、[1, 2, な, に, か, 3]
のような配列ができるというものである。今回の例では [this].concat(args).map( ... )
とできるところで、[this, ...args].map( ... )
としている。なお、関数呼び出しにおける展開は Firefox 27 にて導入されるらしい。
今回使用した、rest パラメータ、アロー関数、展開演算子は ECMAScript 6 の先行実装である。
追記。式クロージャは Firefox 60 で死んでしまったようなので。 Array.slice
なんかも死んでる模様。
Array.prototype.zip = function Array_zip(...args){ return this.map((e, i) => [this, ...args].map(a => a[i])); }