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

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

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

この章では関数とスコープの関係を中心に、スコープとはどのような働きをしていて、スコープ内では変数の名前から取得する値がどのように決まるかを見ていきます。

スコープとは

スコープとは変数や関数の引数などを参照できる範囲を決めるものです。

function fn() {
    const x = 1;
    // fn関数のスコープ内のため`x`は参照できる
    console.log(x); // => 1
}

console.log(x);
上記はスコープの外のためReferenceErrorとなる    

メモ

  • 関数の{}内で定義した変数(x)をスコープの外で呼び出そうとした場合のエラー
    ReferenceError: x is not defined
  • 同じスコープ内で同じ変数名は使用出来ません。(SyntaxError)
  • 関数によるスコープのことを関数スコープと呼びます。

ブロックスコープ

{}←これで囲われた範囲をブロックスコープと呼びます。関数のブロックに限らず、ブロック内で宣言された変数は、スコープ内でのみ参照でき、スコープの外側からは参照できません。if文などのブロックもそうです。

メモ

  • ブロックによるスコープのことをブロックスコープと呼びます。
  • for文は、ループごとに新しいブロックスコープを作成します。
  • ブロックの後はセミコロンは不要です。

スコープチェーン

内側から外側のスコープへと順番に変数が定義されているか探す仕組みのことをスコープチェーンと呼びます。

  • ネストしたブロックの内側をINNERブロックスコープと呼びます。
  • 対して外側をOUTERブロックスコープと呼びます。
{
    OUTERブロックスコープ
    const x = "x";
    {

        INNERブロックスコープからOUTERブロックスコープの変数を参照できる
        console.log(x); // => "x"
    }
}
  • INNER,OUTER,一番外側のスコープという順に探索してどこにもなかったらReferenceError を返す。
  • 外側からは内側の変数を参照出来ません。
  • それぞれのスコープに定義されている変数を優先して参照します。INNERに定義した変数をINNERで出力する場合はINNERの変数を出力し、OUTERで定義した変数をOUTERで出力する場合はOUTERの変数が優先で出力されます。  
{
    // OUTERブロックスコープ
    const x = "outer";
    {
        // INNERブロックスコープ
        const x = "inner";
        // 現在のスコープ(INNERブロックスコープ)にある`x`を参照する
        console.log(x); // => "inner"
    }
    // 現在のスコープ(OUTERブロックスコープ)にある`x`を参照する
    console.log(x); // => "outer"
}

グローバルスコープ

グローバルスコープとは名前のとおりもっとも外側にあるスコープで、プログラム実行時に暗黙的に作成されます。

グローバルスコープで定義した変数はグローバル変数と呼ばれ、グローバル変数はあらゆるスコープから参照できる変数となります。

グローバルスコープには自分で定義したグローバル変数以外に、プログラム実行時に自動的に定義されるビルトインオブジェクトがあります。

このように内側のスコープで外側のスコープと同じ名前の変数を定義することで、外側の変数が参照できなくなることを変数の隠蔽(shadowing)と呼びます。

// ビルトインオブジェクトは実行環境が自動的に定義している
// どこのスコープから参照してもReferenceErrorにはならない
console.log(isNaN); // => isNaN
console.log(Array); // => Array
console.log(String); // => String
console.log(Function); // => Function

これらビルドインオブジェクトは同じ名前で変数定義すると上書きされてしまいます。

// "Array"という名前の変数を定義
const Array = 1;
// 自分で定義した変数がビルトインオブジェクトより優先される
console.log(Array); // => 1

この事を変数の隠蔽(shadowing)と呼びます。

この問題を回避する方法としては、むやみにグローバルスコープへ変数を定義しないことです。グローバルスコープでビルトインオブジェクトと名前が衝突するとすべてのスコープへ影響を与えますが、関数のスコープ内では影響範囲がその関数の中だけにとどまります。

ビルトインオブジェクトと同じ名前を避けることは難しいです。 なぜならビルトインオブジェクトには実行環境(ブラウザやNode.jsなど)がそれぞれ独自に定義したものが多く存在するためです。 関数などを活用して小さなスコープを中心にしてプログラムを書くことで、ビルトインオブジェクトと同じ名前の変数があっても影響範囲を限定できます。

まとめ

  • 基本的にはブロックなどでスコープの影響範囲を制御しない場合はグローバルスコープに影響していることになります。
  • グローバル変数はあらゆるスコープで参照出来るため便利です。
  • ビルドインオブジェクトをshadowingしてしまわないように注意が必要です。

復習

ビルドインオブジェクトとは

JavaScriptで以下のようなあらかじめ定義されているもので、宣言なしで使用することができるものです。

for of と for in

どちらも配列に繰り返し処理をすることができます。
- for of は要素
- for in はindex

const array = [1, 2, 3];
for (const value of array) {
    console.log(value);
}
// 1
// 2
// 3

const array = [1, 2, 3];
for (const value in array) {
    console.log(value);
}
// 0
// 1
// 2
forEach

配列を関数の引数にクレーンのように一つずつ要素を入れます。

Array.forEach((x) => {繰り返しの処理}); // => Arrayの中の配列の要素を一つずつ引数に渡して中身の処理をします。

参考

JS primer 関数とスコープ