🔝

JavaScript は全ての変数を巻き上げる

2021/07/20に公開約1,700字

概要

JavaScript は var, function 以外の変数も巻き上げる。

const f = () => {
  console.log(a)
  console.log(b)
  console.log(c)
}

let   a = 'A'
const b = 'B'
class c {}

f()
結果
A
B
class c {}

説明

巻き上げ (英語: Hoisting) は、JavaScript における「変数をスコープの先頭で宣言されたかのように扱う」挙動のことです。

// ← global
{
  // ← local
  let local
}
let global

実用的 (?) な例として function で宣言した関数を前方から呼び出すものが有名です。

f()  // Hello

function f () {
  console.log('Hello')
}

巻き上げの対象は宣言のみで、初期化や代入は反映されません。
例えば var で宣言と初期化を行った変数を前方から参照すると undefined が返ります。

console.log(x)  // undefined

var x = 'init!'

初期化前の変数は使いどころが無く、意図せず参照しかねないので let, const, class では、参照するとエラーが発生するようになっています[1]

console.log(x)  // Error

let x = 'init!'
結果
Uncaught ReferenceError: Cannot access 'x' before initialization
-> 初期化前の 'x' にアクセスできない

未宣言時のエラーメッセージ[2]と見比べると分かりやすいですが、宣言の巻き上げ自体は var, function 同様に行われています。

未宣言時
Uncaught ReferenceError: x is not defined
-> x が定義されていない

よって、表記上での前方にて変数を参照することも参照が行われる時点において初期化されていれば可能です。

const f = () => {
  // 表記上では前方からの参照
  console.log(x)
}

let x = 'init!'

// 実際に参照するタイミングは初期化後
f()  // init!

使い道としては「補助関数の記述位置に拘りがあって並び替えたい」場合や「相互再帰な関数の定義」などが考えられそうです。

モジュール
export const main_func = () => {
  sub_func()
}
const sub_func = () => {
  // ...
}
相互再帰
const main_func = () => {
  // ...
  sub_func()
}
const sub_func = () => {
  // ...
  main_func()
}

以上、変数の巻き上げの解説と実用できそうな例の紹介でした。

end

脚注
  1. https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Errors/Cant_access_lexical_declaration_before_init ↩︎

  2. https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Errors/Not_defined ↩︎

GitHubで編集を提案

Discussion

ログインするとコメントできます