ホイスティングとは
JavaScriptは、関数内でvarで宣言した全ての変数が先頭で宣言したことになる。
この、宣言が先頭に移動する仕様をホイスティング、または宣言の巻き上げと呼ぶ。
宣言のみが先頭に移動し、代入された変数は移動しないため、
直感的ではない動作をするケースが有る。
変数の巻き上げ
var scope = 'global'; function test() { // 関数内のscopeの宣言のみが巻き上がるので // undefinedになる console.log(scope); // 宣言は巻き上がっているので代入のみ行われる var scope = 'local'; // 代入されたlocalが出力される console.log(scope); } test();
見た目上は最初のconsole.log(scope)
ではglobal
が出力されそうだが、
undefined
が出力される。
その原因は、test()
が実際には下記のコードと等価になるせいだ。
function test() { // 宣言のみ先頭に移動 var scope; // 値は代入されていないのでundefined console.log(scope); scope = 'local'; // 代入されたlocalが出力される console.log(scope); }
ES2015の場合
実は、ES2015以降ではホイスティングを考慮せず記述することができる。
var
ではなくlet
またはconst
で宣言を行うと、宣言より前に使用された変数は
ReferenceError
が出力されるようになった。
var scope = 'global'; function test() { // letで変数が宣言された場合、 // 宣言は巻き上がらずReferenceErrorとなる console.log(scope); let scope = 'local'; console.log(scope); } test();
関数の巻き上げ
関数については、function
文で宣言したものについては、その定義ごと巻き上がる。
変数に保持した場合は、変数のときと同様の動作となる。
function
文で宣言した場合
定義ごと宣言が巻き上がるため、実行することができる。
function hoge() { // fuga()の定義も巻き上がっているので、 // 使用することができる。 fuga(); function fuga() { console.log('fuga'); } } hoge();
var
で宣言した場合
宣言のみ巻き上がるが、実行不可能。
function hoge() { // fugaの宣言のみ巻き上がるが、 // undefinedのため関数として実行することができず // TypeErrorが発生する fuga(); var fuga = function () { console.log('fuga'); } } hoge();
let
・const
で宣言した場合
巻き上がらないため、実行不可能。
function hoge() { // letで宣言しているため定義が巻き上がらず、 // ReferenceErrorが発生する fuga(); let fuga = () => { console.log('fuga'); } } hoge();