Array.prototype.slice.call(arguments)とは
Array.prototype.slice.call(arguments);
っていう、いかにもJavaScriptを使いこなしている風なこの書き方。
もちろん最初はまったく意味がわからんかったです。
そして次に、意味はわかっても、使い道がわからんかったです。
けど最近、使い道がやっと見えてきました。
というわけで、その過程をメモ。
arguments
ざっくり引数のこと。
var func1 = function(name, arg1, arg2) { console.log(arguments); // ["Test", 100, 200] }; var func2 = function(name) { console.log(arguments); // ["Test2", true, function] }; func1('Test', 100, 200); func2('Test2', true, function() {});
関数の実行スコープ内で勝手に作られるローカル変数で、それぞれ引数が配列っぽく格納されてる。
「配列っぽく」というのは、実際のArrayのインスタンスではないから。
DOMのNodeListみたく、配列っぽく使いたいのに使えないコって感じ。
Array.prototype.slice.call()
Array.prototype.****.call()
そこでコレ。
var divTags = document.querySelectorAll('div'); // divTags.forEach(function(e, i) { console.log(i + ': ' + e); }); // ってしたいのにできないので Array.prototype.forEach.call(divTags, function(e, i) { console.log(i + ': ' + e); });
これと同じようなもので、本家Arrayのちからを借りたい時に使う。
Array.slice()
var arr1 = [1, 2, 3]; var arr2 = arr1.slice(); var arr3 = arr1.slice(1); console.log(arr2); // [1, 2, 3] console.log(arr3); // [2, 3] arr2 = arr3 = []; console.log(arr1); // [1, 2, 3]
配列のコピーが欲しい時に使うやつですね。
コピーといえば、Array.sliceはシャローコピーで云々かんぬんというのがある気がするけど、
本筋とはあまり関係がない気がするのと、うまく説明できる気がしないので・・気になるけど頭の隅にそっと置いておく。
というわけで、以下の謎だったコードは、
Array.prototype.slice.call(arguments);
argumentsを配列に変換してるコードということがわかりました。
あとの引数はコピーを開始する位置ってことで、そのまんまsliceですね。
ちなみに、Array.prototype.** と [].** は同じ動きなのでお好きな方を。
短く書ける方がイケてるとかいう俗説があるらしい。
つかいみち
さてさて、理屈はわかったけど使い道がわからんわ!
ってのは割とよくある気がしてて、せっかくわかったなら是非モノにしたい!というわけで調べました。
Backbone.js
// Backbone.Events.triggerの実装(関係ないとこは削ってます) trigger: function(name) { var args = slice.call(arguments, 1); // つかってるー! if (!eventsApi(this, 'trigger', name, args)) return this; var events = this._events[name]; if (events) triggerEvents(events, args); return this; },
なるほど。
明示的に受けたいのは、なんてイベント(name)をtriggerするかだけ。
後の引数はよしなに処理を渡したいので・・って感じですね、ふむ。
Underscore.js
// _.delayの実装より _.delay = function(func, wait) { var args = slice.call(arguments, 2); // つかってる! return setTimeout(function(){ return func.apply(null, args); }, wait); };
これも同じで、本来実装したい挙動に関するものにフォーカスして関数の実装は書く。
それでも引数は渡ってくることもあるし、ただ流せばいいだけやし、みたいな時に使うんですね。
ちなみに
そもそもこれはargumentsに限った話ではなくて、
いわゆるArrayライクなオブジェクトなら・・OKっていう。
var obj = { '0': 'zero', '1': 'one', '2': 'two', '3': 'three', '4': 'four', length: 5 }; Array.prototype.slice.call(obj); // ["zero", "one", "two", "three", "four"]
うん、ひとつかしこくなりました。
以下、直接関係ないけどいろいろ見てた記事さまたち
参考:$.extend()とディープコピーを理解しよう - slowjet
参考:Backbone.Modelのattributesにオブジェクト入れるときの注意 - Webtech Walker
参考:aheckmann/sliced