【JavaScript入門】関数

## JavaScriptにおける関数

* 「データ型の一種」であり、変数やオブジェクトと同様に扱うことができる。
* 関数を引数やオブジェクトのプロパティにセットすることもできる
* 変数に代入して使いまわすことができる
* 関数を代入する変数はconstで定義するのが主流

## 関数の定義方法

### function命令

``` javascript
function calcTriangleArea(base, height) {
    return base * height / 2;
}
```
return命令が省略された場合はundefined（未定義値）を返す。

### 関数リテラル（アロー関数）

``` javascript
let getTriangle = (base, height) => {
    return base * height / 2;
};
console.log('三角形の面積：' + getTriangle(5, 2));

// ブロックを表す {...} と return命令の省略
let getTriangle = (base,height) => base * height / 2;

// 引数が１個の場合には、引数をくくるカッコも省略できます
let getCircle = radius => radius * radius * Math.PI;

// 引数がない場合は、カッコを省略することはできません。
let show = () => console.log('こんにちは、世界！');
```

#### その他特記事項

* 関数リテラルは実行時（代入時）に評価される
* functon命令はコードを解析／コンパイルするタイミングで、関数を登録している  
  → 実行時にはすでにコード内の構造の一部として、関数をどこからでも呼び出すことができる
* return命令の直後で改行すると、後続の式が無視される

## 引数について

### ES2015以前の環境

* 与える引数の数と関数側で要求する数が同じかどうかチェックされない（エラーにならない）
* 呼び出しの際に不足している引数はundefinedとして扱われる
* 呼び出し元から与えられた引数はargumentsオブジェクトを使用して取得することできる

``` javascript:script.js
function showMessage(value) {
    if (arguments.length != 1) {
        throw new Error('引数の数が間違っています: ' + arguments.length);
    }
    console.log(value);
}

try {
    showMessage('山田', '鈴木');
} catch(e) {
    window.alert(e.message);
}
// 同じ要領で、引数のデータ型や値の有効範囲などの妥当性をチェックすることもできる
function getTriangle(base, height) {
    if (base === undefined) { base = 1; }
    if (height === undefined) { height = 1; }
    return base * height / 2;
}

console.log(getTriangle(5));

// 引数baseだけを省略することはできない。「省略できるのは、あくまで後ろの引数だけ」

// 可変長引数の関数を定義する
function sum() {
    var result = 0;
    // 与えられた引数を順番に取り出し、順に加算処理
    for (var i = 0, len = arguments.length; i < len; i++) {
        var tmp = arguments[i];
        if (typeof tmp != 'number') {
            throw new Error('引数が数値ではありません: ' + tmp);
        }
        result += tmp;
    }
    return result;
}

try {
    console.log(sum(1, 3, 5, 7, 9));
} catch(e) {
    window.alert(e.message);
}
```

明示的に宣言された引数と可変長引数を混在させる

``` javascript
function printf(format) {
    //引数の2番目以降を順番に処理
    for(var i = 1, len = arguments.length; i < len; i++) {
        var pattern = new RegExp('\\{' +(i - 1) + '\\}', 'g');
        format = format.replace(pattern,arguments[i]);
    }
    console.log(format);
}
printf('こんにちは、{0}さん。私は{1}です。','掛谷','山田');
```
### 名前付き引数
呼び出し時に名前を明示的に指定できる引数のこと

メリット

* 引数が多くなっても、コードの意味がわかりやすい
* 省略可能な引数をスマートに表現できる
* 引数の順番を自由に変更できる

``` javascript
getTriangle({height: 4}) // 前方の引数だけを省略
getTriangle({height: 4, base: 5}) // 引数の順番を変更した
```

有効なケース

* 引数の数が多い場合
* 省略可能な引数が多く、省略パターンにもさまざまな組み合わせがある

``` javascript
// 引数をオブジェクトリテラル（ここでは仮引数args）で受け取る
functiongetTriangle(args) {
    // オブジェクトのプロパティとして、それぞれの引数にアクセス
    if(args.base === undefined) {
        args.base = 1;
    }
    if(args.height === undefined) {
        args.height = 1;
    }
    return args.base * args.height / 2;
}
// 「{...}」の記述はオブジェクトリテラルを表す
console.log(getTriangle({base: 5, height: 4})); //結果：10
```

## ES2015における引数の記法

### 引数のデフォルト値

``` javascript:script.js
function getTriangle(base = 1, height = 1){
    return base * height / 2;
}

console.log(getTriangle(5)); //結果：2.5

// デフォルト値にはリテラルだけでなく、ほかの引数、関数（式）の結果などを指定することもできる
function multi(a, b = a){
    return a * b;
}

console.log(multi(10,5)); //結果：50
console.log(multi(3)); //結果：9（引数bの値はaと同じ3）

// 、ほかの引数をデフォルト値とする場合、参照できるのは、自身より前に定義されたものだけ
function multi(a = b, b = 5){
    return a * b;
}

console.log(multi());
```

* デフォルト値が適用されるのは、引数が明示的に渡されなかった場合だけ
* null／false／0／空文字列など、意味的に空を表すような値でも、それらが明示的に渡された場合は、デフォルト値は適用されない
* undefined（未定義）を引数に渡した場合には、引数は渡されなかったものと見なされ、デフォルト値が適用される


``` javascript
function getTriangle(base = 1, height = 1){
    return base * height / 2;
}
console.log(getTriangle(5, null));
```

### 可変長引数

ES2015では、仮引数の前に「...」（ピリオド3個）を付与することで、可変長引数（Rest Parameter）となる。渡された任意個数の引数を、配列としてまとめて受け取ることができ、関数が可変長引数を受け取ることがわかりやすい。可変長引数は、真正のArrayオブジェクトなのでpush/shiftなどのメソッドを利用できる。

``` javascript
function sum(...nums) {
    let result = 0;
    for(let num of nums){
        if(typeof num !== 'number'){
            throw new Error('指定値が数値ではありません：' + num);
        }
        result += num;
    }
    return result;
}
try{
    console.log(sum(1, 3, 5, 7, 9));
} catch(e) {
    window.alert(e.message);
}}
```