JavaScriptのスコープについてまとめました。
スコープとは
スコープとは、実行中のコードから変数や関数などの値と式が参照できる範囲のこと。スコープの内側では参照ができ、スコープの外からスコープに定義されている変数は参照できない。
異なるスコープなら同じ変数名や関数名を定義できる。
スコープの種類
グローバルスコープ
グローバルスコープはプログラム実行時に暗黙的に作られる一番外側のスコープで、グローバルスコープに定義された変数はプログラムのどこからでも参照できる。
const a = 'global' function fn() { function fn2() { function fn3() { // 関数の中の関数の中の関数の中からでも参照できる console.log(a) // global } fn3() } fn2() } fn()
関数スコープ
関数スコープは関数内に作られるスコープ。
function fn() { // 関数スコープ var a = 0 let b = 0 const c = 0 } // 関数の外から参照するとエラーになる console.log(a) // Uncaught ReferenceError: a is not defined console.log(b) // Uncaught ReferenceError: b is not defined console.log(c) // Uncaught ReferenceError: c is not defined
関数内で定義した関数を外からは参照できない、
function fn() { console.log('fn') function fn2() { console.log('fn2') } } fn() // fn fn2() // Uncaught ReferenceError: fn2 is not defined
関数スコープ内に変数を定義することによってグローバルスコープの汚染を防ぐことができるので、関数内でしか使わない変数は関数内に定義する。
ブロックスコープ
ブロックスコープは{}
の中のスコープで、if
文やwhile
文もブロックスコープにあたる。
ブロックスコープ内でvar
を使って定義した変数は外から参照できてしまうので、let
もしくはconst
を使うようにする。
{ // ブロックスコープ var a = 0 let b = 1 const c = 2 } // varで定義した変数は参照できてしまう console.log(a) // 0 // let、もしくはconstで定義した変数は外から参照できない console.log(b) // Uncaught ReferenceError: b is not defined console.log(c) // Uncaught ReferenceError: c is not defined
ブロックスコープ内の関数宣言は外から参照でき、let
、const
を使った関数式は参照できない。
{ // 関数宣言 function fn() { console.log('fn') } // 関数式 const fn2 = function() { console.log('fn2') } } fn() // fn fn2() // Uncaught ReferenceError: fn2 is not defined
レキシカルスコープ
レキシカルスコープは、コードを書く場所によって参照できる変数が変わるスコープのこと。実行中のコードから見た外部スコープを指す。
コードが実行されるタイミングではなく、コードを記述したタイミングでスコープが決定される。
変数a
を参照する関数fn2
を関数fn
の中に記述すると参照できる。
function fn() { const a = 0 function fn2() { // 外側のスコープに定義されているので参照できる console.log(a) // 0 } fn2() } fn()
しかし、関数fn2
をグローバルスコープで定義して実行するとエラーになる。
function fn() { const a = 0 } fn() function fn2() { // aは別の関数スコープの中に定義されているため参照できない console.log(a) // Uncaught ReferenceError: a is not defined } fn2()
このようにコードを記述する場所が変わることで参照できる変数も変化する。
また、レキシカルスコープは関数を実行した時点ではなく、定義した時点でスコープが決まる。
const a = 0 function fn() { console.log(a) // 0 } function fn2() { const a = 1 // fnを定義した時点で0を参照しているので、1にならない fn() } fn() fn2()
スコープチェーン
スコープチェーンとは、内側のスコープから一番近い外側へのスコープへと順番に参照できる変数を探す仕組みのこと。
別のスコープで同じ名前の変数が定義されている場合は、内側から優先して参照される。
// グローバルスコープ const a = 'global' // ブロックスコープ { const a = 'block' // 一番近いblockを参照 console.log(a) // block // 関数スコープ function fn() { const a = 'function' // 一番近いfunctionを参照 console.log(a) // function } fn() // 関数スコープは無視して一番近いblockを参照 console.log(a) // block } // ブロックスコープは無視して一番近いglobalを参照 console.log(a) // global
まとめ
- グローバルスコープにはなるべく変数を定義しない
- 内側のスコープから一番近い外側のスコープから順番に参照できる変数を探していき、なければ未定義のエラーを返す