Node.js 関数

関数は、特定のタスクを実行するための再利用可能なコードブロックです。

Node.js では、関数は JavaScript の中核となる構成要素の一つであり、アプリケーションを構築する基本的な単位です。

Node.js は JavaScript のすべての関数の特性を継承しており、その非同期プログラミングモデルにおいて重要な役割を果たしています。

JavaScript では、関数は別の関数の引数として使用できます。関数を先に定義してから渡すこともできますし、引数を渡す場所で直接関数を定義することもできます。

関数の重要性

  • コードの再利用:同じロジックを繰り返し記述するのを避ける
  • モジュール化:複雑な問題を小さな関数に分解する
  • 保守性:独立した機能ユニットのデバッグや修正が容易になる

関数宣言

使用functionキーワードで関数を宣言します。

function greet(name) {
    console.log(`Hello, ${name}!`);
}

関数式

関数を変数に代入します。

const greet = function(name) {
    console.log(`Hello, ${name}!`);
};

アロー関数

ES6 で導入された簡潔な関数式です。

const greet = (name) => {
    console.log(`Hello, ${name}!`);
};

// 单行箭头函数
const greet = name => console.log(`Hello, ${name}!`);

関数の種類

1、通常の関数

最も一般的な関数形式で、引数と戻り値を持つことができます。

function add(a, b) {
    return a + b;
}

2、無名関数

名前のない関数で、通常は他の関数に引数として渡されます。

setTimeout(function() {
    console.log('This is an anonymous function.');
}, 1000);

3、コールバック関数

別の関数に引数として渡され、何らかの操作が完了した後に呼び出されます。

function fetchData(callback) {
    setTimeout(() => {
        const data = 'Some data';
        callback(data);
    }, 1000);
}

fetchData((data) => {
    console.log(data);
});

非同期関数

コールバックから Promise へ。

例

function readFilePromise(path) {
  return new Promise((resolve, reject) => {
    fs.readFile(path, 'utf8', (err, data) => {
      if (err) reject(err);
      else resolve(data);
    });
  });
}

async と await キーワードを使用して非同期操作を処理します。

例

async function fetchUser(id) {
    try {
        const response = await fetch(`https://api.example.com/users/${id}`);
        const user = await response.json();
        return user;
    } catch (error) {
        console.error('Error fetching user:', error);
    }
}

fetchUser(1).then(user => console.log(user));

呼び出し方法の比較

呼び出し方法 例 特徴
直接呼び出し greet('Alice') 最も一般的な方法
メソッドとして呼び出し obj.method() this は呼び出し元のオブジェクトを指す
コンストラクター呼び出し new Constructor() 新しいインスタンスを作成する
間接呼び出し greet.call(null, 'Bob') this の指す先を変更できる

高度な使い方

1、クロージャ

クロージャとは、関数が自身の語彙スコープを記憶しアクセスできることを指し、その関数が語彙スコープの外で実行される場合でも同様です。

例

function createCounter() {
    let count = 0;
    return function() {
        count++;
        return count;
    };
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2

2、高階関数

関数を引数として受け取るか、関数を返す関数です。

例

function applyOperation(a, b, operation) {
    return operation(a, b);
}

const sum = applyOperation(5, 3, (x, y) => x + y);
const product = applyOperation(5, 3, (x, y) => x * y);

console.log(sum); // 8
console.log(product); // 15

関数の引数の処理

デフォルト引数

関数宣言時に引数にデフォルト値を提供します。

function greet(name = 'Guest') {
    console.log(`Hello, ${name}!`);
}

greet(); // Hello, Guest!
greet('Alice'); // Hello, Alice!

残余引数

不特定多数の引数を配列として表現できるようにします。

例

function sum(...numbers) {
    return numbers.reduce((acc, num) => acc + num, 0);
}

console.log(sum(1, 2, 3, 4)); // 10

分割代入引数

オブジェクトや配列からデータを抽出して変数に代入します。

例

function getUserInfo({ name, age }) {
    console.log(`Name: ${name}, Age: ${age}`);
}

const user = { name: 'Alice', age: 30 };
getUserInfo(user); // Name: Alice, Age: 30

例

関数の単一責任:各関数は1つのことだけを行うべきであり、そうすることでテストと保守が容易になります。

例

function calculateArea(width, height) {
    return width * height;
}

グローバル変数を避ける:グローバル変数の使用を最小限に抑え、ローカル変数と関数の引数を使用します。

例

function calculateTotal(items) {
    let total = 0;
    for (const item of items) {
        total += item.price * item.quantity;
    }
    return total;
}

アロー関数を使用する:アロー関数は簡潔で明確であり、特にコールバック関数を扱う際に便利です。

例

const users = [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' }
];

const names = users.map(user => user.name);
console.log(names); // ['Alice', 'Bob']

エラーハンドリング:try...catch 文を使用して、発生する可能性のあるエラーを処理します。

例

async function fetchUser(id) {
    try {
        const response = await fetch(`https://api.example.com/users/${id}`);
        if (!response.ok) {
            throw new Error('Network response was not ok');
        }
        const user = await response.json();
        return user;
    } catch (error) {
        console.error('Error fetching user:', error);
    }
}

関数を引数として渡す:関数を引数として使用します。

例

function say(word) {
  console.log(word);
}

function execute(someFunction, value) {
  someFunction(value);
}

execute(say, "Hello");

上記のコードでは、say 関数を execute 関数の最初の変数として渡しています。ここで渡しているのは say の戻り値ではなく、say そのものです!

これにより、say は execute 内のローカル変数 someFunction になり、execute は someFunction() (括弧付きの形式)を呼び出すことで say 関数を使用できます。

もちろん、say には変数があるため、execute は someFunction を呼び出す際にそのような変数を渡すことができます。

匿名関数:関数を変数として渡すことができます。しかし、「先に定義してから渡す」という回り道をする必要はありません。別の関数の括弧内で直接関数を定義して渡すことができます:

例

function execute(someFunction, value) {
  someFunction(value);
}

execute(function(word){ console.log(word) }, "Hello");

execute が最初の引数を受け取る場所で、execute に渡す関数を直接定義しています。

この方法では、この関数に名前を付ける必要さえありません。これが匿名関数と呼ばれる理由です。


関数の受け渡しがどのようにHTTPサーバーを機能させるのか

この知識を踏まえて、シンプルながらも奥深いHTTPサーバーを再度見てみましょう:

例

var http = require("http");

http.createServer(function(request, response) {
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.write("Hello World");
  response.end();
}).listen(8888);

これでかなり明確に見えるはずです:createServer 関数に匿名関数を渡しています。

このようなコードでも同じ目的を達成できます:

例

var http = require("http");

function onRequest(request, response) {
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.write("Hello World");
  response.end();
}

http.createServer(onRequest).listen(8888);

関数のベストプラクティス

関数設計の原則

  1. 単一責任:1つの関数は1つのことだけを行う
  2. 適切な命名:動詞+名詞の形式を使用します(例:getUserInfo)
  3. 長さの制御:コードは20行を超えないように推奨
  4. 副作用を避ける:純粋関数はテストと保守が容易です

パフォーマンス最適化のテクニック

例

// クロージャを使用して結果をキャッシュする
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;
  };
}

実践演習

演習1:温度変換関数を作成する

例

/**
* 摂氏と華氏を相互に変換する
* @param {number} temp - 温度値
* @param {string} unit - 元の単位 ('C' または 'F')
* @returns {number} 変換後の温度
 */

function convertTemperature(temp, unit) {
  // あなたのコード...
}

演習2:シンプルなイベントエミッターを実装する

例

class EventEmitter {
  constructor() {
    this.events = {};
  }
 
  // on/emit/off メソッドを実装する
}
その他の拡張