32415


可変長の引数が難しくてわかりません。

下記の部分を記載しなくて済むのがメリットのようですが、難しくて初心者にはわかりません。

[code]

    for (var _len = arguments.length, arg = Array(_len), _key = 0; _key < _len; _key++) {
      arg[_key] = arguments[_key];
    }
    
[/code]

＞＞＞

> 可変長の引数 引数の冒頭に...と記述することにより引数を配列として取得することができます。
[code]

>     var bar = function(...arg){
>       console.log(arg);
>       console.log(Array.isArray(arg));
>     };
>     bar(1,2,3,4);
>  
[/code]

>
> これは以下のようにコンパイルされます。
[code]

>     var bar = function bar() {
>       for (var _len = arguments.length, arg = Array(_len), _key = 0; _key <
> _len; _key++) {
>         arg[_key] = arguments[_key];
>       }
>  
>       console.log(arg);
>       console.log(Array.isArray(arg));
>     };
>     bar(1, 2, 3, 4); このコードを実行した場合はコンソールに以下のようなログが出力されます。
>  
>     [1, 2, 3, 4]
>     true
>  
[/code]

<http://blog.webcreativepark.net/2015/10/29-165605.html>

初心者にもわかるように解説していただけるとありがたいです。  
もしかして上級者になるまで無縁なものなのでそれまで無視していれば良いのでしょうか？


CC BY-SA 3.0
2017-02-05T13:20:04.843
32399
2017-02-05T22:04:02.553
2017-02-05T13:32:15.927
5044
14320
question
javascript
可変長の引数が難しくてわかりません。


[Rest
parameters](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions_and_function_scope/rest_parameters)という構文はECMAScript
2015 (6th Edition, ECMA-262)で標準化されたものです。この構文に限りませんが、ブラウザーによって対応状況が分かれます。

ですので新しい構文に対して、どう接するか

  * 新しい構文を使用せず、動作対象と考えるブラウザーでサポートされている構文のみを使用してコードを記述する
  * 新しい構文を使用するが、必要に応じて既存のブラウザーでも動作可能になるようにコードを変換（質問文中ではコンパイルと表現）する

のアプローチが考えられますが、これについて質問者さんはどうお考えでしょうか？ 前者であればこのような構文については忘れてしまっていいです。後者であれば

> 下記の部分を記載しなくて済むのがメリットのようですが

ではなく動作させるために必要な変換が行われているだけです。


CC BY-SA 3.0
2017-02-05T13:34:21.077
32401
2017-02-05T13:34:21.077
4236
32399
answer


ES6として利用されているわけなので実際に行われる変換は一旦無視して、動作だけ気にすればいいと思うのですが……

可変長引数は **利用者が任意数の引数を指定できる** 引数です。

例えば指定した引数の値の合計を返す関数`sum(...nums)`を考えてみましょう。

期待する動作としては

[code]

    sum(5, 6, 3); // 14
    sum(6, 14, 57, 74); // 151
    
[/code]

のようなものですね。

これはES6のRest parametersを使えば以下のように実装できます。

[code]

    function sum(...nums) {
      let res = 0;
      for(const num of nums) {
        res += num;
      }
      return res;
    }
    
    console.log(sum(5, 6, 3));
    console.log(sum(6, 14, 57, 74));
[/code]

ES6以前でも利用できたArgumentオブジェクトは[
**純粋な配列ではなかった**](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions_and_function_scope/rest_parameters#rest_parameters_%E3%81%A8_arguments_%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E9%81%95%E3%81%84)のがこの記法の場合は純粋な配列で可変長引数を扱えるようになっています。  
( > 下記の部分を記載しなくて済むのがメリットのようですが、 の部分はArgumentオブジェクトを配列に変換しています)

可変長引数の使い方であればWikipediaを始めいろいろな例があると思うのでまずはそれらをみてみ、何がわからないか明確にしていただければ。  
(ここでggrで済ませるのはよくないですけどね。)


CC BY-SA 3.0
2017-02-05T21:29:44.803
32414
2017-02-05T21:29:44.803
2376
32399
answer


可変長引数とは何か？

可変長引数は古くはC言語でも存在する引数の受け取り方です。まずは可変長引数を使わない一般的な関数を考えましょう。一般的な関数では、関数に定義された仮引数の数と関数を呼び出す時の引数の数が同じでなければなりません。

[code]

    // 引数は二つ
    function add(a, b) {
      return a + b;
    }
    const result = add(2, 3); //=> 5
    
[/code]

上記の関数`add()`は二つの仮引数をとるため、呼び出す時も二つの引数が必要です。JavaScriptは引数の数のチェックを行わないので、引数の数が少なかったり、多かったりしてもエラーにはなりませんが、想定外の動作になることでしょう。(言語によっては引数の数が違うとエラーになります)

`add()`は二つの数値の加算でしたが、複数の、なるべく任意の数の数値の加算をするように拡張したいと考えます。どうすればいいでしょうか？JavaScriptでは渡される引数が仮引数よりも少なければ、渡されない仮引数はundefined値になりますので、次のようにできるかも知れません。

[code]

    // 引数の数を増やす
    function add(a, b, c, d, e) {
      if (a == null) a = 0;
      if (b == null) b = 0;
      if (c == null) c = 0;
      if (d == null) d = 0;
      if (e == null) e = 0;
      return a + b + c + d +e;
    }
    const result = add(2, 3); //=> 5
    
[/code]

いや、これでもたった5つしか対応できていません。このような方法で無限に追加していくことは不可能です。となると、任意の数の数値を入れることができる配列として渡すのが現実的です。

[code]

    // 引数は配列
    function add(list) {
      return list.reduce((a, b) => a + b);
    }
    const result = add([2, 3]); //=> 5
    
[/code]

これでいくつでも対応できるようになりましたが、引数として渡すときにわざわざ配列にしてから渡す必要が出てきてしまいます。最初のコードとの互換性も無くなってしまいました。そこで登場するのが可変長引数です。

[code]

    // 可変長引数
    function add(...list) {
      return list.reduce((a, b) => a + b);
    }
    const result = add(2, 3); //=> 5
    
[/code]

配列のときと見比べてください。変わったのは仮引数の書き方と関数の呼び出し方だけです。つまり、可変長引数とは、本来は配列として渡さないとうまくいかないような場合に使用し、複数の引数を自動的に配列にして一つの仮引数に割り当ててしまう動作です。可変長引数を使わなくても、呼び出す時の引数に配列を指定する方法を使えば同じ事ができます。

まとめると、可変長引数は、関数呼び出しの時の渡す引数の指定方法に違いがあるだけで、配列を受け取る引数と等価である、とさえ覚えておけば問題ありません。

さて、この可変長引数はES6から追加された物で、ES5以前は存在しませんでした。では、ES5のときに可変長引数と同じようなことができなかったのかというと、そうではありません。JavaScriptの関数内では`arguments`という特殊なローカル変数が使えるようになっています。この`arguments`には(どのような仮引数を定義しているかには関わらず)関数に渡された全ての引数が収まっています。`arguments`は配列(Array)ではありませんが、配列と同じように添字アクセスと`length`プロパティが利用可能で、ES5以前はこれを使って可変長引数のような機能を実現していました。可変長引数をBabelで変換したときのコードがその使い方の一つと言うことです。

ES6以上を使うのであれば、Babelで変換された後のES5以前の書き方を覚える必要はありませんし、細かく把握する必要もありません。何をしているのかだけを覚えておけば十分です。ES6の仕様通りに動作するよう変換されるのか確認するのはBabelの開発者達の仕事です。ましてや、node.jsやElectronなどES6の可変長引数が直接問題なく動作する環境向けにコーディングする場合は、変換すら不要でしょう。


CC BY-SA 3.0
2017-02-05T22:04:02.553
32415
2017-02-05T22:04:02.553
7347
32399
answer