[EffectiveJavaScript輪読会2]グローバルオブジェクトを使うのは、最小限にとどめる

GAS

どうも。つじけ(tsujikenzo)です。このシリーズでは2021年8月からスタートしました「ノンプロ研EffectiveJavaScript輪読会」についてお送りします。

前回のシリーズでは、第1章に相当する考察をお届けしました。

引き続き、第2章をお送りします。

目次と日程

第2章は「変数のスコープ」です。JavaScriptだけでなく、すべてのプログラミング言語において、避けて通れない概念ですね。

第1章 JavaScriptに慣れ親しむ

第2章 変数のスコープ

第3章 関数の扱い

第4章 オブジェクトとプロトタイプ

第5章 配列とディクショナリ

第6章 ライブラリとAPI設計

第7章 並行処理

LT大会は2021年9月8日です。がんばりましょう。

今日はさっそく1回目で、「グローバルオブジェクトを使うのは、最小限にとどめる」 をお届けします。

テキスト第2章「変数のスコープ」の項目8に対応しています。

今日のアジェンダ

  • スコープのおさらい
  • globalThis

スコープのおさらい

まず、スコープのおさらいです。

中級のみなさんは、変数宣言によりどのようにスコープが生成されるのか、理解していると思います。

ローカル領域で宣言された変数のスコープ

宣言関数スコープ(function)生成ブロックスコープ({})生成
無し××
var×
let
const
関数の仮引数

グローバル領域で宣言された変数は、グローバル変数(グローバルオブジェクト、もしくはスクリプトオブジェクト)になるため、スコープを生成しません。

よく、「グローバル領域に宣言したグローバル変数は、どこからでも呼び出せるが、書き換えもされる」という解説を見かけます。

あまり見かけないのが、「関数宣言内で変数宣言を付けない変数への代入が、グローバル変数になり、思わぬ結果をもたらす」という例です。

デバッガを見ながら、グローバル変数とローカル変数の違いを理解しましょう。

function myFunction2_8_01() {

  flag = true; //varをつけたり外したりして実行しましょう
  const total = getFlag();

  if (flag) console.log('flagが立ってます');
  if (!flag) console.log('flagは降りてます');

}

function getFlag() {

  flag = false; //varをつけたり外したりして実行しましょう

}

テキストP32は、とくに変数iとnがグローバル変数になることで破綻することを表しています。

score()関数のfor文のiの前にletを付けたり外したりして、結果を確認してみましょう。

function myFunction2_08_2() {

  const players = [
    {
      name: 'Tom',
      levels: [
        { score: 100 },
        { score: 200 },
      ]
    },
    {
      name: 'Bob',
      levels: [
        { score: 300 },
        { score: 400 },
      ]
    },
  ];

  const avrageScore = averageScore(players);
  console.log(avrageScore); //300 + 700 / 2 = 500

}


function averageScore(players) {

  sum = 0;
  for (i = 0, n = players.length; i < n; i++) {

    sum += score(players[i]);

  }

  return sum / n;
}

function score(player) {

  sum = 0;
  for (i = 0, n = player.levels.length; i < n; i++) { //iの前にletを付けたり外したりする
    sum += player.levels[i].score;

  }

  return sum;

}

globalThis

thisについてもおさらいしておきましょう。

thisはもともと、書かれた場所で、オブジェクト自身を表すキーワードでした。

グローバル領域や関数内でthisを書くと、thisはグローバルオブジェクトになります。

ということは、thisを使って、グローバルオブジェクトに変数をもたせることもできます。

わたしはECMAScript2018から採用された、globalThis でコーディングしていきたいと思います。

実行ログを確認したら、関数内のコードを、グローバルエリアに移動して実行したりしてみましょう。

function myFunction2_8_03(num) {

  x = 1
  console.log(globalThis.x); //1

  var y = 2;
  console.log(globalThis.y); //undefined

  const z = 3;
  console.log(globalThis.z); //undefined

  globalThis.l = 4;
  console.log(l); //4

}

スコープ表にグローバル変数の挙動を加えたものがこちらです。

宣言関数スコープ(function)生成ブロックスコープ({})生成グローバル変数の生成
無し××
var×〇(グローバルエリアで記述)
let△(グローバルエリアで記述)
const△(グローバルエリアで記述)
関数の仮引数×

まとめ

以上で、「グローバルオブジェクトを使うのは、最小限にとどめる」をお届けしました。

実務を意識するなら、getterやsetterとグローバル変数の使いどころになってきますが、これらはクラスの項目で考察したいと思います。

次回は、「ローカル宣言は、必ず宣言しよう」 をお届けします。

このシリーズの目次

[EffectiveJavaScript輪読会2]グローバルオブジェクトを使うのは、最小限にとどめる 

[EffectiveJavaScript輪読会2]ローカル宣言は、必ず宣言しよう 

[EffectiveJavaScript輪読会2]クロージャと仲良くしよう 

[EffectiveJavaScript輪読会2]変数の巻き上げ(ホイスティング)を理解する 

[EffectiveJavaScript輪読会2]ローカルスコープを作るには即時関数式(IIFE)を使おう

[EffectiveJavaScript輪読会2]名前付き関数式のスコープは可搬性がないので注意しよう 

[EffectiveJavaScript輪読会2]ブロックローカルな関数宣言のスコープも可搬性がないので注意しよう

タイトルとURLをコピーしました