Closed1

スコープ

ユイト🍓ユイト🍓

スコープ(Scope)とは?

実行中のコードから見て参照(使用)できる変数や関数の範囲のこと。
例えば、関数内で定義された変数は関数外で使用することはできない。

function fnA () {
 const val = 100;
}

function fnB() {
 console.log(val) 
}

fnB() //  Uncaught ReferenceError: val is not defined

スコープの種類

  • ブロックスコープ
  • グローバルスコープ
  • スクリプトスコープ
  • 関数スコープ
  • モジュールスコープ

変数や関数は上記スコープのいずれかに必ず属する。
基本的に変数や関数は同一スコープ内に存在する場合に参照(使用)可能である。
もし、親のスコープがあれば親スコープに書かれた変数や関数を使用することもできる。

スコープの性質

  • スコープの階層化(子スコープから親スコープへの参照ができる。親から子のスコープは参照できない)
  • 記述された時点で、ある識別子がどこのスコープの変数を参照するのか決定する。(静的スコープ)

関数スコープ

関数内で宣言された変数や関数、関数の引数が属するスコープ。
基本的に異なる関数スコープに属する変数や関数を参照できない。

function fnA () {
 const val = 100;
}

function fnB() {
 console.log(val) 
}

fnB() //  Uncaught ReferenceError: val is not defined

ただ、スコープが階層化されている場合、子スコープから親スコープの変数や関数を参照できる。
親スコープから子スコープや、兄弟間のスコープで変数や関数を参照できない。

function fnA() {
  //関数スコープ(親スコープ)
  let valA = 1;
  console.log(valA); // 1

  function fnB() {
    //関数スコープ(fnAの子スコープ)
    const valB = 10;
    console.log(valA); // 1
    console.log(valB); //  10
    return (valA += 1);
  }

  function fnC() {
    //関数スコープ(fnAの子スコープ)
    const valC = 100;
    console.log(valC) // 100
    console.log(`関数fnBの実行結果: ${fnB()}`); // 関数fnBの実行結果: 2
    console.log(`変数numberの値: ${valA}`); // 変数numberの値: 2
    console.log(valB); // 参照できない。valBは関数fnB内でのみ参照できる
  }

  return fnC();
}

fnA();

上記のコードで各スコープにおいて参照できる変数や関数を画像で表してみた。
fnBやfnCの中で同一の関数を実行(再帰関数)すると、無限ループになるためif文などを使って実行を停止するための処理を記述する。

ブロックスコープ

JavaScriptでは「{ }(波括弧またはブレイス)」のことをブロックと呼ぶ。このブロック内で、letまたはconstを使って宣言された変数や関数が属するスコープのこと。

{
// ブロックスコープ
 let number = 1;
 const string = 1;
 const fn = () => {
   return 1;
 }
}

一般的にブロック単体で使用せず、if文やfor文と一緒に使用する。


if(true) {
 //ブロックスコープ
 const number = 1;
}

注意点として、ブロック内でvarを使って変数や関数を宣言したり、functionキーワードを使って関数を宣言(関数宣言文)してもブロックスコープには含まれず、一つ外側のスコープに含まれます。(関数スコープの場合は、varを使って変数や関数を宣言しても関数スコープに含まれます。)

//一つ外側のスコープ(今回の場合、グローバルスコープ)
if(true) {
 // varやfunctionキーワードの場合、に属する。
 var number = 1;
 function fn() {
  return 1;
 } 
}

console.log(number) // 1 グローバルスコープに属するため、実行できてしまう
console.log(fn())  //  1 上記と同様

"use strict"を使った厳格モードの場合、関数宣言文はブロックスコープに含まれます。varは厳格モードでも一つ外側のスコープに含まれます↓

"use strict";
//グローバルスコープ
if(true) {
 var number = 1; // グローバルスコープのまま
 function fn() { 
 // ブロックスコープに属する
  return 1;
 } 
}

console.log(number) // 1 グローバルスコープに属するため、実行できてしまう
console.log(fn())  // ブロックスコープに属しており、スコープ外で実行しているためエラー  ReferenceError: fn is not defined

ブロックスコープも関数スコープと同様にスコープが階層化されている場合、子スコープは親スコープの変数や関数を参照できる。

グローバルスコープ

JavaScriptファイルのトップレベルまたは、scriptタグ直下に記述したコード内で、「varを使って定義された変数や関数」または「関数宣言文(function 関数名 () {...})によって定義された関数」が属するスコープ。
グローバルスコープに配置された変数や関数は、コードのどこからでも参照できる。

トップレベルとは、関数やブロックで囲まれていない範囲のこと。具体的には、JavaScriptファイル直下のコードやscriptタグ直下のコードのこと。

<script>
// トップレベル
var globalVal = "グローバル変数";
var globalFn = () => {
 return "グローバル関数式"
}

function callGlobal() {
 console.log(globalVal,globalFn()) // グローバル変数、グローバル関数式
 return "グローバル関数宣言文";
} 

console.log(callGlobal()); // グローバル関数宣言文

</script>

注意点として、グローバルスコープに配置された変数や関数は、異なるscriptタグから参照可能です。また、<script src="jsファイル">のようにファイルを読み込む形にした場合も同様です。他のファイルの変数名と衝突して意図しない挙動が発生する可能性(グローバル汚染)があります。Viteやwebpackなどを使用すると、一部のグローバル汚染を防いでくれます。
https://ja.stackoverflow.com/questions/91570/webpackを使用すればグローバル汚染は起こりませんか

スクリプトスコープ

javascriptファイルのトップレベル、またはscriptタグ直下に記述したコード内で、「letやconstを使って宣言された変数や関数」が属するスコープ。

// jsファイルのトップレベル
let number = 1;

const string = "文字列"

const fn = () => {
 return "関数式"
}

グローバルスコープと同様に、スクリプトスコープに配置された変数や関数はどこからでも参照可能です。
グローバルスコープとの違いは、グローバルスコープはブラウザの実行環境ではWindowオブジェクトのプロパティに格納されますが、スクリプトスコープは格納されません。

//スクリプトスコープ
const a = 1;
//グローバルスコープ
var b = "string";
// console.log(window.b or b)で呼び出せる
window.a = 100;
console.log(window.a)  // 1

動画でも確認

モジュールスコープ

scriptタグにtype属性moduleを設定された、javascriptファイルのトップレベルまたはscriptタグ直下で宣言された変数や関数が属するスコープ。
モジュールスコープは、scriptタグの単位でモジュールが形成され、他のモジュール内の変数や関数は参照できない。

<script type="module">
//モジュールスコープ
const number = 1;
console.log(number) // 1
</script>

<script type="module">
//モジュールスコープ
console.log(number) // エラー モジュールスコープでは、異なるscriptタグやjsファイルから参照不可。
</script>
このスクラップは2023/06/03にクローズされました