TypeScriptに入門したいのでJavaScriptに入門してみる(関数と宣言)

続: JavaScript入門

教材は

https://jsprimer.net/

関数と宣言

関数は function キーワードを使う。

function function_name(arg1, arg2) {
    // something to do...
    return return_value;
}

// 関数の呼び出し

const result = function_name(arg1, arg2);

関数名のルールは変数名のルールと同じ

  1. 半角のアルファベット、_ (アンダースコア)、$ (ダラー)、数字を組み合わせた名前にする
  2. 変数名は数字から開始できない
  3. 予約語と被る名前は利用できない
  • returnは必須ではない(値を返す必要がない関数はreturnを書く必要はない)
  • returnの返り値を省略している場合、もしくはreturn 文のそのものを省略した場合は未定義の値である undefined を返す。

関数の引数

定義した関数の仮引数よりも呼び出し時の引数が少ない場合、余った仮引数には undefined という値が代入される。(エラーにならないんですね…)

function echo(x) {
    return x;
}
console.log(echo(1)); // -> 1
console.log(echo()); // -> undefined

また、引数の数が多い時は余分な引数は単純に無視される。

[ES2015] デフォルト引数

Pythonなどと同じくデフォルト引数が使える。

function echo(x = "hoge") {
    return x;
}
console.log(echo(1)); // -> 1
console.log(echo()); // -> hoge

デフォルト引数が導入されるまではOR演算子を使ったデフォルト値の指定がよく利用されていたらしいがバグを生みやすいので使わない方が良さそう。

可変長引数

可変長引数を使うには Rest parameters を使うか、arguments という特殊な変数を利用する。

[ES2015] Rest parameters

function fn(...args) {
    console.log(args); // -> ["a", "b", "c"];
}

fn("a", "b", "c");

...args と書かれている部分がRest parametersと呼ばれる可変長引数の部分。argsはRest parametersの名前なので、すきな変数名が使えるっぽい。

function fn(arg1, ...restArgs) {
    console.log(arg1); // -> "a"
    console.log(restArgs); // -> ["b", "c"];
}

fn("a", "b", "c");

通常の仮引数と組み合わせて定義することもできるが、Rest parametersは 必ず末尾の引数にしなければならない。

function fn(x, y, z) {
    console.log(x);
    console.log(y);
    console.log(z);
}

const array = [1, 2, 3];

// Spread構文で配列を引数に展開して関数を呼び出す
fn(...array);

// 次のように書いたのと同じ意味
fn(array[0], array[1], array[2]);

Spread構文は、 配列の前に ... をつけた構文 のことで、関数には配列の値を展開したものが引数として渡される。

arguments

Rest parameters を使えない場合のみ利用すべき機能が arguments

可変長引数を扱う別の方法として、 arguments という関数の中のみで参照可能な特殊な変数がある。 arguments は関数に渡された引数の値がすべて入った Array-like なオブジェクトで、配列のようにインデックスで要素へアクセスができるが、 Array ではないので、実際の配列とは異なり Array のメソッドは利用できないという特殊なオブジェクトである。

function fn() {
    // `arguments` はインデックスを指定して各要素にアクセスできる
    console.log(arguments[0]); // -> "a"
    console.log(arguments[1]); // -> "b"
    console.log(arguments[2]); // -> "c"
}
fn("a", "b", "c");

[ES2015] 関数の引数と分割代入

function printUserId({ id }) {
    console.log(id);
}

const user = {
    id: 42
};

printUserId(user);

関数の引数に分割代入を使うことができる。 分割代入を使わなかった場合は以下のような書き方になる。

function printUserId(user) {
    console.log(user.id);
}

const user = {
    id: 42
};

printUserId(user);
function print([first, second]) {
    console.log(first); // -> 1
    console.log(second); // -> 2
}
const array = [1, 2];
print(array);

関数はオブジェクト

関数はオブジェクトとして参照できる(関数オブジェクトという) () をつけなければ関数オブジェクトとして参照できる。

function fn() {
    console.log("hoge");
}
const myFunc = fn;
myFunc();

関数が値として扱えることを ファーストクラスファンクション(第一級関数) と呼ぶ。

関数式

関数式とは関数を値として変数へ代入している式のことを言う。

無名関数ですね。

const func_name = function() {
    // something to do
    return return_value;
};

再帰の例でよくある累乗を求める場合の、関数式に名前をつけて利用する例。

// factorialは関数の外から呼び出せる名前
// innerFactは関数の外から呼び出せない名前
const factorial = function innerFact(n) {
    if (n === 0) {
        return 1;
    }
    // innerFactを再帰的に呼び出している
    return n * innerFact(n - 1);
};
console.log(factorial(3)); // => 6

関数式に名前をつけた場合は 関数の中からのみ 関数名を呼び出すことができる。

[ES2015] Arrow Function

const function_name = () => {
    // something to do 
    return return_value;
};

Arrow で関数を定義できる。(C言語のポインタ参照時に使う矢印とは意味が違う) Arrow Functionには書き方にいくつかのパターンがある。

  • 仮引数が1つのときは () を省略可能
  • 関数の処理が1つの式である場合に、ブロックとreturn文を省略可能
    • その式の評価結果を return の返り値とする。

様々な Arrow Function の書き方

const fnA = () => { /* 仮引数がない場合 */ };
const fnB = (x) => { /* 仮引数が1つの時 */ };
const fnC = x => { /* 仮引数が1つのときは()を省略可能 */ };
const fnD = (x, y) => { /* 仮引数が複数の時 */ };

値の返し方

// この2行は一緒
const mulA = x => { return x*x; }; 
const mulB = x => x*x; 

Arrow Functionの特徴として、

  • 常に無名関数
  • this が静的に決定できる
  • function キーワードと比べて短く書ける
  • new できない(コンストラクタ関数ではない)
  • arguments 変数を参照できない

といったものが上げられる。

arrayの中身を全て2倍する関数を function キーワードとArrow Functionで比べてみる。

const array = [1, 2, 3];

// function版
const doubleArray_f = array.map(function(value) {
    return value * 2;
});


// Arrow版
const doubleArray_a = array.map( value => value*2 );

Arrow Functionは引数や処理が1つなら、とか arguments が使えないとか条件が多い分、人による解釈や実装の違いが生まれにくいという特徴がある。

同じ名前の関数宣言は上書きされる

functionキーワードでの関数っ宣言と var キーワードを使った関数式のみで発生する。

うーん、エラーにしてほしい、、、

コールバック関数

引数として渡される関数のことをコールバック関数と呼ぶ。コールバック関数を引数として使う関数やメソッドのことを高階関数と呼ぶ。

function 高階関数(コールバック関数) {
    コールバック関数();
}

他の言語にもよくありますね。

メソッド

オブジェクトのプロパティである関数をメソッドと呼ぶ。JavaScriptにおいては 関数とメソッドの機能的な違いはない

const obj = {
    method1: function() {
        // functionのメソッド
    },
    method2: () => {
        // Arrow Functionのメソッド
    }
};

// 空オブジェクトを作った後で定義することもできる

const obj2 = {};
obj2.method = function() {
};

// メソッドの呼び出し
obj.method();

[ES2015] メソッドの短縮記法

const obj = {
    method() {
        return "this is method.";
    }
};
console.log(obj.method()); // -> "this is method."

この書き方はオブジェクトのメソッドだけではなく、クラスのメソッドと共通の書き方である。 他の言語と同じような書き方だと思う。