JavaScriptにおける複数の変数への代入のショートハンドとその弊害

株式会社アビストAIソリューション事業部 AI-Plant Bamboo 開発チームの杉山です。

突然ですが、問題です。

JavaScriptのコード

問題

以下のJavaScriptのコードについて、 (1)~(3)の行を実行した場合、それぞれの実行結果はどのようになるでしょうか?

(() => {
    const a = b = c = 'Bamboo';
})();

console.log(a); // (1)
console.log(b); // (2)
console.log(c); // (3)




↓↓↓ 解答は記事を下にスクロールしたところにあります ↓↓↓

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

解答

正解は、

  1. Uncaught ReferenceError: a is not defined
  2. Bamboo
  3. Bamboo

です。

解説

問題のポイントは2箇所です。

  • (A) 変数のスコープ
  • (B) 未宣言の変数への代入

まず、(A)変数のスコープについてですが、上記のコードにおいて、変数aのみがローカルスコープ(関数内でのみアクセス可能)です。 変数bと変数cはグローバルスコープ(関数外からアクセス可能)です。

JavaScriptにおいて、代入演算子は右側から順に評価され、次のように処理されます。

const a = b = c = 'Bamboo';
  • イ) 値'Bamboo'が変数cに代入される
  • ロ) 変数cの値が変数bに代入される
  • ハ) 変数bの値が変数aに代入され、変数aがconstで宣言される。

ここで、変数bと変数cは、変数宣言されません。 これが(B)未宣言の変数への代入です。

JavaScriptでは、未宣言の変数はグローバル変数として定義されます。 よって、変数bと変数cはグローバル変数になり、変数aだけがローカル変数になります。

以上のような理由で、関数外からそれぞれの変数にアクセスしようとした場合、変数aは参照エラー、変数bと変数cにはアクセスできる、という状態になります。

複数の変数への代入のショートハンド

const a = b = c = 'Bamboo';のようなショートハンドは、複数の変数に同じ値を代入したいときに便利ですが、 その動作を理解していない場合、上記のように、意図せずして名前空間を汚染する可能性があります。

それでもショートハンドを使いたい場合には、代わりに、以下のように記述することで、 名前空間を汚染することなく、記述量を削減できます。

(() => {
    const a = 'Bamboo',
          b = 'Bamboo',
          c = 'Bamboo';
})();

// 関数外からはアクセス不可能になる
console.log(a); // (1) Uncaught ReferenceError: a is not defined
console.log(b); // (2) Uncaught ReferenceError: b is not defined
console.log(c); // (3) Uncaught ReferenceError: c is not defined

まとめ

今回はJavaScriptの代入のショートハンドとそれに伴う弊害について書きました。

リファクタリングの際にこのようなショートハンドを使いたい箇所があり、 JavaScriptでそういったことが可能なのかを検証していて発見したのですが、 恥ずかしながら、実際に動かしてみるまで、今回紹介したような動作をするとは知りませんでした。

改めて言語仕様を読み、理解を深める必要があると感じました。

株式会社アビストAIソリューション事業部では、このような技術者向けの記事を今後も公開する予定です。 Twitterもやっていますので、ぜひフォローしてください。

記事に関するご指摘や、コメントもお待ちしております。

AIソリューション事業部 公式Twitterアカウント @abist_ai

参考:

変数の宣言: https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Grammar_and_Types

代入演算子: https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Expressions_and_Operators#Assignment_operators