JavaScript | 関数の定義と呼び出し位置

JavaScript JavaScript
スポンサーリンク

例1:関数宣言のホイスティング(復習)

console.log(foo()); // "I'm foo"

function foo() {
  return "I'm foo";
}
JavaScript

説明:function foo はホイスティングされる → 呼び出しが先でも動く。

例2:関数式 (const) はホイスティングしない

try {
  console.log(bar()); // ReferenceError または TypeError(実行環境次第)
} catch (e) {
  console.log('エラー:', e.message);
}

const bar = function() {
  return "I'm bar";
};
JavaScript

説明:barconst に代入される「値」。代入前は使えない。

例3:var と巻き上げ(挙動が微妙)

console.log(baz); // undefined(宣言は巻き上げられるが、値は未初期化)
try {
  console.log(baz()); // TypeError: baz is not a function
} catch (e) {
  console.log('エラー:', e.message);
}

var baz = function() { return 'baz'; };
JavaScript

説明:var 宣言は巻き上げられて undefined になるので、関数としては使えない。

例4:アロー関数と関数式

// NG
try {
  console.log(arrow(2));
} catch (e) {
  console.log('エラー:', e.message); // arrow is not defined / cannot access before initialization
}

const arrow = (n) => n * 2;
console.log(arrow(2)); // 4
JavaScript

説明:アロー関数も関数式なので定義より前に呼べない。

例5:HTMLで <script> を分けたときの実例

(ファイルではなく1つのHTMLで試してみて)

<!-- head側 -->
<script>
  init(); // この行が先に実行される → error になる場合がある
</script>

<script>
  function init() {
    console.log('init() 実行');
  }
</script>
HTML

修正版(安全パターン):

<body>
  <button id="btn">押して</button>

  <script>
    function init() {
      document.getElementById('btn').addEventListener('click', () => {
        show();
      });
    }

    function show() {
      alert('押されたよ!');
    }

    // DOMが準備できたら init を呼ぶ
    window.addEventListener('DOMContentLoaded', init);
  </script>
</body>
HTML

説明:DOMContentLoaded を使うと、DOM要素が無いタイミングでエラーになるのを防げる。

例6:ネストした関数(スコープ)とクロージャ

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

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

説明:内側の関数が外側の count を覚えている(クロージャ)。外部から直接 count は見えない。

例7:内部関数を外に「公開」する(Revealing Module Pattern)

const myModule = (function() {
  let secret = 'shh';
  function privateFn() { return 'private'; }
  function publicFn() { return `public + ${privateFn()}`; }
  return { publicFn }; // private は外に出さない
})();

console.log(myModule.publicFn()); // "public + private"
console.log(myModule.privateFn);  // undefined
JavaScript

説明:モジュール内のヘルパーを隠して安全に公開できる。

例8:よくあるミス — 関数を返す時の注意

function makeGreeter(name) {
  // NG: 関数を返す時に () を付けると即時実行される
  // return function() { console.log('Hi ' + name); }(); // これはダメ

  // 正しい:関数そのものを返す
  return function() { console.log('Hi ' + name); };
}

const greeter = makeGreeter('Ken');
greeter(); // "Hi Ken"
JavaScript

説明:返すべきは「関数オブジェクト」そのもの。() を付けると呼んでしまう。

例9:イベントハンドラを後で定義するパターン(安全)

<button id="btn">Click</button>
<script>
  // 関数宣言はボタンのクリック時に呼ばれるので、スクリプト内のどこにあってもOK
  document.getElementById('btn').addEventListener('click', handleClick);

  function handleClick() {
    alert('clicked');
  }
</script>
HTML

説明:イベント登録は DOMがあることが前提。スクリプト位置に注意(body末尾かDOMContentLoadedを推奨)。

練習問題(手を動かそう)

  1. 次を実行して何が出る?理由も書いてみて。
console.log(x); 
var x = 5;
console.log(x);
JavaScript
  1. 次のコードをどう修正すると Hello Alice を表示する?
console.log(greet('Alice'));
const greet = name => 'Hello ' + name;
JavaScript
  1. outer の中で定義した inner を外から使いたい。ふたつの方法を書いてみて(ヒント:返す or グローバルに代入)。

解答(簡潔)

  1. undefined(最初) → 5(2回目)。var は宣言だけ巻き上げられる。
  2. greetconst から先に定義する、または function greet(name){...} と宣言に変える。
  3. (A) return inner; で関数を返す。 (B) window.inner = inner; のようにグローバルに代入(ただし推奨されない)。

タイトルとURLをコピーしました