🪆

関数の中の関数について【超初心者向けの記事】「ネストされた関数」「入れ子の関数」「ローカル関数」とも言う

2025/02/03に公開

さて今回は、JavaScriptやPythonで一般的に用いられる
「ネストされた関数」(「入れ子の関数」、「ローカル関数」)についてです。

C言語を中心にやっていた元組み込み系エンジニアは
この記述がはじめサッパリわからなかったのでまとめてみました。
(C言語ではネストされた関数の記述は不可能のようです)

「ネストされた関数」のメリット

JavaScriptやPythonで「ネストされた関数」を使う主なメリットは

  1. スコープを制限できる
  2. クロージャの活用(外側の変数を使える)
    というのがあります。

JavaScriptの例

スコープの制限

document.addEventListener('DOMContentLoaded', function() {
    const modal = document.getElementById('eventModal');
    
    // この関数はこのスコープでしか使えない
    function closeModal() {
        modal.style.display = 'none';
    }
    
    // closeModalはこの中でしか使えないので、
    // 他のコードから誤って呼び出されることがない
    window.onclick = function(event) {
        if (event.target == modal) {
            closeModal();
        }
    }
});

クロージャの活用(外側の変数を使える)

function createCounter() {
    let count = 0;  // 外側の変数
    
    // この関数は外側のcountを使える
    function increment() {
        count++;
        console.log(count);
    }
    
    return increment;
}

const counter = createCounter();
counter();  // 1
counter();  // 2

この機能により

  • コードの整理がしやすくなる
  • 変数のスコープを制限できる
  • 関連する処理をまとめやすくなる
    といった利点があります。

Pythonの例

スコープの制限

def process_order():
    items = get_items()
    
    # この関数はprocess_order内でしか使えない
    def calculate_tax(price):
        return price * 0.1
    
    total = 0
    for item in items:
        tax = calculate_tax(item.price)
        total += item.price + tax
    
    return total

クロージャの活用

def create_counter():
    count = 0
    
    def increment():
        nonlocal count  # Pythonでは外側の変数を変更する場合nonlocalが必要
        count += 1
        print(count)
    
    return increment

counter = create_counter()
counter()  # 1
counter()  # 2

ただし、小さな違いもあります。

  • Pythonでは外側の変数を変更する場合、nonlocal宣言が必要
  • JavaScriptではvar, let, constでスコープが変わりますが、Pythonではそういった違いはない
  • Pythonではデコレータを作る際にネストされた関数がよく使われる(これはJavaScriptにはない使い方)

デコレータとは?

デコレータは、関数やクラスの動作を変更したり拡張したりするためのPythonの機能です。

簡単な例を見てみましょう

def print_function_name(func):
    def wrapper():
        print(f"関数名: {func.__name__}が実行されます")
        func()  # 元の関数を実行
        print("関数の実行が終わりました")
    return wrapper

# デコレータを使用
@print_function_name
def hello():
    print("Hello!")

# hello()を実行すると...
hello()

このコードを実行すると

関数名: helloが実行されます
Hello!
関数の実行が終わりました

が出力されます。

Djangoでは、よく使われるデコレータの例として:

from django.contrib.auth.decorators import login_required

@login_required
def my_view(request):
    # このビューはログインしているユーザーだけがアクセスできる
    return render(request, 'my_template.html')

このように、@login_requiredをつけることで、そのビューにアクセスするには事前にログインが必要になります。

デコレータを使うメリットは

  1. コードの再利用が簡単
  2. 既存の関数の動作を変更せずに、新しい機能を追加できる
  3. コードがきれいで読みやすくなる

ということがあります。

おしまいに

  • C++: ラムダ式で似た機能を実現
  • C#: 可能だが一般的ではない
    ということですが、「ネストされた関数」をソースで見かけたことが無かったので
    今回のJavaScriptやPythonの記述については学びになりました。

基礎中の基礎かもしれず、
「そんなことも知らんのか」という声が飛んできそうですが、
「まだまだ伸びしろがある」と謎にポジティブに考えて
4月からのWeb系開発現場への転職に備えたいと思います(大丈夫か)

Discussion