JavaScript再入門 #2

スコープについて

  • 実行中のコードから値と式が参照できる範囲

JSの場合、5種類のスコープが存在する

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

グローバルスコープ

  • varfunctionで宣言を行った場合は windowオブジェクトのプロパティとして制限される
  • windowオブジェクトとはグローバルスコープのことを指す
  • グローバルオブジェクトで宣言されているオブジェクトは window を省略できる
var a = 1;

console.log(a); // 1
console.log(window.a); // 1

スクリプトスコープ

  • letconst で制限を行った場合は、Script側のプロパティとして制限される

関数スコープ

  • 下記のように関数内に定義される変数のことで、関数の外からは呼び出すことができない。
function a () {
  let b = 0;
  console.log(b); // 0
}

console.log(b); // Error

ブロックスコープ

  • 波括弧で囲んだ中(ブロック)であれば、値の取得を行うことができる。
  • ブロック内に functionvar を利用すると、ブロックスコープが無視される。
{
  let a = 0;
  console.log(a); // 0

  const b = 1;
  console.log(a); // 1

	var c = 2;
	console.log(c); // 2

	function d() {
		console.log('3');
	}
	d(); // 3

	const e = function(){
		console.log('4');
	}
	e(); // 4
}

console.log(a); // Error
console.log(a); // Error
console.log(c); // 2 (副作用: ブロックスコープが無視される)
d(); // 3 (副作用: ブロックスコープが無視される)
e(); // Error

// ブロックスコープは基本的にif文など一緒に生成する

if (true) {
	const c = 2;
	console.log(c);
}

スコープと実行コンテキスト

  • スコープとは「実行中のコードから見える範囲」
  • 実行コンテキストとは「コードが実行される状況」

レキシカルスコープ

  • レキシカルスコープとは
    • 実行中のコードから見た 外部スコープ のこと
    • どのようにしてスコープを決定するかの仕様のことで静的スコープとも呼ばれる
// 自身のスコープより外側のスコープは参照可能なため、外部スコープと呼ばれる

// グローバルスコープ - a, fn1
let a = 2;
function fn1() {

	// 関数スコープ - b, fn2
  let b = 1;
  function fn2() {

		// 関数スコープ - c 
    let c = 3;
    console.log(b);
  }
  fn2();
};
fn1();

スコープチェーン

スコープが複数階層で、連なっている状態のこと(上記のコードのように)

  • 同名の変数がスコープの内側に存在する場合は、内側で定義された値を取得する
// スコープは外側から
// グローバルスコープ > スクリプトスコープ > 関数スコープ = ブロックスコープ

let a = 2;
window.a = 4;
function fn1() {
  let a = 1;
  function fn2() {
    let a = 3;
    console.log(a); // 3
  }
  fn2();
};
fn1();

クロージャーとは

レキシカルスコープの変数を関数が利用している状態のこと

let a = 2;
function fn1() { // aの値を保持している <= クロージャー
  let b = 1;
  function fn2() { // a, bの値を保持している <= クロージャー
    let c = 3;
    console.log(b);
  }
  fn2();
};
fn1();

プライベート変数

  • 下記はクロージャー利用して、関数スコープ内でしか参照できない変数を定義している
function incrementFactory() {
    // クロージャーを使ったプライベート変数になっている
    let num = 0;

    function increment() {
        num = num + 1;
        console.log(num);
    }

    return increment;
}

// const incrementを定義したときに、incrementFactoryが実行される
const increment = incrementFactory();

// returnで返却された increment関数の実行処理のみが実行される
increment(); // 1
increment(); // 2
increment(); // 3
increment(); // 4

動的な関数の生成

  • 下記はクロージャー利用して、関数スコープ内でしか参照できない引数を定義している
function addNumberFactory(num) {
    function addNumber(value) {
        return num + value; 
    }
    return addNumber;
}

// "関数を作成する関数" に値を渡すことで、動的な関数を生成する事ができる
const add5 = addNumberFactory(5); 
const add10 = addNumberFactory(10);
const result = add10(10);
console.log(result); // 20

即時関数 (IIFE)

関数定義と同時に一度だけ実行される関数。

  • 関数の中で定義された要素と、外で定義された要素を明確に分けたいときに利用する
let result = (function(仮引数) {
	return 戻り値;
})(実引数);

() はJavaScriptでは下記2つの意味がある。

  • 関数の実行
    • 関数名の後に () をつけることで関数を実行する
  • グループ化
    • let = b (1 + 2) * 3; のような優先度をあげるための要素