Array.prototype.slice についての考察(というか妄想)のはなし
関数の引数は、配列っぽいけど配列じゃないため扱いにくいから、配列に直す場合があって
function sortedArrayFromArgs () { var slice = Array.prototype.slice; // (1) var args = slice.apply(arguments); // (2) args.sort(function (a, b) { return (a === b) ? 0 : (a < b) ? 1 : -1; }); return args; } var $sorted = sortedArrayFromArgs(-123, 123, 345, -987); console.log($sorted); // [ -987, -123, 123, 345 ]
上の例だと、可変長の引数をソート済みの配列に直して返しています。
この時に (1) と (2) で、配列に直すテクニックは、結構知られているけど、どうして、関数の引数が配列になるのかは、あまり紹介されていませんよね。
ということで、以下は考察
- 1) slice メソッドは、呼ばれた時に空の配列を作ります。そして、this の指す配列の一部(引数を指定しないか、0 を指定すると全部)をコピーする
- 2) Array.prototype.slice 関数は this が指すオブジェクトは配列以外でもOK. 乱暴な言い方すると「配列っぽい実装のオブジェクト」ができていればいいらしい。
- 3) Arguments オブジェクトは、この「配列っぽい実装のオブジェクト」に該当するので、 this の代わりに arguments を適用することで( (2) の個所)配列になる。
という寸法らしい。
ということで、「配列っぽい」オブジェクトを作ってためしてみたらどうか? という実験
かなり無理やりな実装方法にしてみた
function LikeArray (first, second, third) { this[0] = first; this[1] = second; this[2] = third; } (function (P) { P.length = function () { var i = 0; for (var p in this) { i++; } return i; }(); P.slice = Array.prototype.slice; // thisの対象にちゅうもくしてね })(LikeArray.prototype); var likeArrayObject = new LikeArray('abc', "DEF", "kiss"); console.log(likeArrayObject); // { '0': 'abc', '1': 'DEF', '2': 'kiss' } (A) var cloneLikeArrayObject = likeArrayObject.slice(0); console.log(cloneLikeArrayObject); // [ 'abc', 'DEF', 'kiss' ] (B) // 配列ならソートできるはず console.log(cloneLikeArrayObject.sort(function (a, b) { a = a.toUpperCase(); b = b.toUpperCase(); return (a === b) ? 0 : (a < b) ? 1 : -1; })); // [ 'kiss', 'DEF', 'abc' ]
- (A): new LikeArray(...) したオブジェクトは「配列っぽいオブジェクト」で、"length" プロパティを持つ。また、"slice" メソッドを持つ
- (B): sliceメソッドを使って「配列っぽいオブジェクト」を「配列オブジェクト」に直したもの
になった