株式会社アビストAIソリューション事業部 AI-Plant Bamboo 開発チームの杉山です。
突然ですが、問題です。
問題
以下のJavaScriptのコードについて、 (1)~(3)の行を実行した場合、それぞれの実行結果はどのようになるでしょうか?
(() => { const a = b = c = 'Bamboo'; })(); console.log(a); // (1) console.log(b); // (2) console.log(c); // (3)
↓↓↓ 解答は記事を下にスクロールしたところにあります ↓↓↓
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
解答
正解は、
- Uncaught ReferenceError: a is not defined
- Bamboo
- 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