6時だョ!!全員集合!!

Rails・JavaScrictを中心にアウトプットします。

2021年12月16日 JavaScript (JS Primer) 関数とスコープ

[コラム]変数を参照できる範囲を小さくする

変数のスコープとは、その変数が参照できる範囲のことです。

スコープには、大きく分けて以下の2種類があります。

  • グローバルスコープ → ページ全体でどこからでも参照できます。
  • ローカルスコープ → ページ内の部分的な範囲のみ参照できます。
    変数はできるだけ利用するスコープ内に定義する方が良いです。

    現在のスコープの変数を参照するつもりがグローバル変数を参照したり、その逆も起きることがあるからです。 あらゆる変数がグローバルスコープにあると、どこでその変数が参照されているのかを把握できなくなります。 これを避けるシンプルな考え方は、変数はできるだけ利用するスコープ内に定義するというものです。

グローバルスコープにはグローバルスコープに定義する必要性があるものだけにすれば良いという事です。

以下はアンチパターンです。

function doHeavyTask() {
    // 計測したい処理
}
const startTime = Date.now();
doHeavyTask();
const endTime = Date.now();
console.log(`実行時間は${endTime - startTime}ミリ秒`);

このコードでは、計測処理以外で利用しないstartTimeとendTimeという変数がグローバルスコープに定義されています。

プログラム全体が短い場合はあまり問題になりませんが、プログラムが長くなっていくにつれ影響の範囲が広がっていきます。 この場合は関数のブロック内で変数を定義する方が好ましいです。

// 実行時間を計測したい関数をコールバック関数として引数に渡す
const measureTask = (taskFn) => {
    const startTime = Date.now();
    taskFn();
    const endTime = Date.now();
    console.log(`実行時間は${endTime - startTime}ミリ秒`);
};
function doHeavyTask() {
    // 計測したい処理
}
measureTask(doHeavyTask);

その他 理由

  • 変数名の衝突が発生しやすくなるため、注意を払う必要があります。(保守性が落ちてしまう)
  • javascriptの新しいプラグインを入れた場合などに競合が発生し、予期せぬ動きになる場合があります。

復習 メモ

  • コールバック関数とは関数の引数に指定出来る別の関数です。
  • 関数を呼びだす際に関数を引数に渡せる(値として扱える)のはファーストクラスファンクションであるためです。

関数スコープとvarの巻き上げ

letはvarを改善する目的で導入された構文ですが、既存のコードや既存のライブラリなどではvarが利用されている場面もあるため、varの動作を理解する必要があります。

letとvarの違い

変数を宣言する前にその変数を参照する

// var宣言より前に参照してもエラーにならない
console.log(x); // => undefined
var x = "varのx";

// letの場合はエラーになります。
console.log(x); // => ReferenceError: can't access lexical declaration `x' before initialization
let x = "letのx";
  • letの場合・・・ReferenceErrorの例外が発生して参照できません。
  • varの場合・・・暗黙的にundefinedを返します。
    変数xを参照するコードより前に変数xの宣言部分が移動しているためです。

上記のvarの挙動をコード例として記述すると以下のようになります。

// 解釈されたコード
// スコープの先頭に宣言部分が巻き上げられる
var x;
console.log(x); // => undefined
// 変数への代入はそのままの位置に残る
x = "varのx";
console.log(x); // => "varのx"

undefinedになる理由は宣言部分だけが巻き上げが行われていて、変数への代入はそのままの位置に残るためです。

var pokemon = 'ピカチュー';  //グローバル変数
function show(){
    console.log(pokemon);
    var pokemon = "ポリゴン"
} // ポリゴンを再代入 巻き上げが起こるためundefined

show(); 

varを使わなければいい話なのですが、既存のコードや既存のライブラリなどではvarが利用されている場面もあるためこのような違いを認識しておく必要があります。

参考

JS primer [コラム] 変数を参照できる範囲を小さくする ピカチューカイリュー