JavaScript

関数

JavaScript

関数宣言、アロー関数、クロージャ

関数の定義方法

宣言、式、アロー関数

function-types.js javascript
// 関数宣言(巻き上げあり)
function greet(name) {
    return `こんにちは、${name}!`;
}

// 関数式(巻き上げなし)
const greet = function(name) {
    return `こんにちは、${name}!`;
};

// アロー関数
const greet = (name) => `こんにちは、${name}!`;

// アロー関数 — オブジェクト返却
const makeUser = (name, age) => ({ name, age });

// アロー関数 — 複数行
const add = (a, b) => {
    const result = a + b;
    return result;
};

// デフォルト引数
function connect(host = 'localhost', port = 3000) {
    return `${host}:${port}`;
}

// 残余引数(rest parameters)
function sum(...nums) {
    return nums.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3, 4); // 10

クロージャ

外部スコープの変数を参照し続ける関数

closures.js javascript
// カウンター
function makeCounter(start = 0) {
    let count = start;
    return {
        increment() { return ++count; },
        decrement() { return --count; },
        value()     { return count; },
    };
}
const counter = makeCounter(10);
counter.increment(); // 11
counter.increment(); // 12
counter.value();     // 12

// プライベート変数のエミュレーション
function createUser(name) {
    let _password = null;    // 外からアクセス不可
    return {
        getName: () => name,
        setPassword: (pw) => { _password = pw; },
        verify: (pw) => pw === _password,
    };
}

// メモ化
function memoize(fn) {
    const cache = new Map();
    return (...args) => {
        const key = JSON.stringify(args);
        if (cache.has(key)) return cache.get(key);
        const result = fn(...args);
        cache.set(key, result);
        return result;
    };
}

高階関数

関数を引数・戻り値として扱う

higher-order.js javascript
// 関数を引数に取る
function applyTwice(fn, value) {
    return fn(fn(value));
}
applyTwice(x => x * 2, 3); // 12

// 関数を返す
function multiply(factor) {
    return (n) => n * factor;
}
const double = multiply(2);
const triple = multiply(3);
double(5); // 10
triple(5); // 15

// カリー化
const add = (a) => (b) => a + b;
const add5 = add(5);
add5(3); // 8
add5(7); // 12

// 関数合成
const compose = (...fns) => (x) => fns.reduceRight((v, f) => f(v), x);
const pipe    = (...fns) => (x) => fns.reduce((v, f) => f(v), x);

const process = pipe(
    x => x.trim(),
    x => x.toLowerCase(),
    x => x.replace(/\s+/g, '-'),
);
process('  Hello World  '); // 'hello-world'

this の束縛

call、apply、bind、アロー関数での this

this.js javascript
const obj = {
    name: 'Alice',
    // 通常の関数: this は呼び出し元に依存
    greet() {
        return `こんにちは、${this.name}!`;
    },
    // アロー関数: this を外側のスコープから継承
    greetArrow: () => {
        return `こんにちは、${this?.name}!`; // this は obj ではない
    },
};
obj.greet(); // 'こんにちは、Alice!'

// call: 即時実行、引数をカンマ区切り
obj.greet.call({ name: 'Bob' }); // 'こんにちは、Bob!'

// apply: 即時実行、引数を配列で渡す
Math.max.apply(null, [1, 5, 3]); // 5

// bind: 新しい関数を返す(this を固定)
const greetAlice = obj.greet.bind({ name: 'Charlie' });
greetAlice(); // 'こんにちは、Charlie!'

// イベントリスナーでの注意
button.addEventListener('click', obj.greet.bind(obj));