memolu

いろいろメモってます

ES6 - let & const を解説してみる

本連載について

第一回はlet & constを学びます。 本連載では少しづつES6について記載していこうと思います。 発表されてから久しいですが、まだ習得されていない方は今からでも勉強していきましょう。

ES6とは

ES6はES2015とも呼ばれ、その名の通り2015年に発表されたjavascriptの新基準です。 非常に野心的ともとれる新機能の数々は、これからのjavascriptの未来を明るくしていると言えるでしょう。 各ブラウザのサポート状況は、以下のリンクより確認することができます。

ECMAScript 6 compatibility table
https://kangax.github.io/compat-table/es6/

↑を見ていただくとわかる通り、現状モダンブラウザですら完全にサポートできているとは言えません。 IEのシェアなどを考えれば、このまま使用するのは時期尚早なことが明白です。

トランスパイラの存在

ES6でのコーディングはブラウザサポートの問題を回避するためにトランスパイラ(コード変換ツール)を用いることが慣例化されています。 おそらく世界的に一番多く使用されているのは『babel』でしょう。

babeljs.io

このツールを使うことでES6で書いたコードはES5のコードで出力することができるようになります。 開発時はES6でコードを書き、クライアントサイドで実行されるファイルはES5として出力するという仕組みです。 babelが素晴らしいのはES6で記載したコードには何も設定する必要がないという点でしょう。 つまり、ブラウザサポートの問題が解決した段階で、babelをプロジェクトから削除すればいいのです。

babelの使い方については今回の趣旨とはずれますので割愛させていただきます。 これは別の記事でしっかりまとめたいと思います。

letとconstによる変数宣言

前置きが長くなりましたが、今回はletとconstを勉強してみようと思います。 letとconstは変数宣言分であるvarに置き換わるものです。

そしてletとconstは以下のような違いがあります。

let: 値の変化する変数
const: 値の変わらない変数(つまり定数)

このように値が変わるものと変わらないものを宣言時に明確化することにより、 コードにおける読みやすさの向上を期待しています。

また、letとconstで宣言された変数は、ブロックスコープ内で有効な変数になります。 つまりif文やfor文の中だけで有効な変数を宣言することが容易になりました。 varを使用した場合は、関数スコープのみを対象としていましたので、これは大きな進歩と言えるでしょう。

letを学ぶ

基本

さっそくletを使って実際に簡単なコードを記載してみましょう。 varと比べつつ結果を出力して差分を見ていきます。

'use strict';

var foo = 1;
let bar = 1;

console.log(foo); // 1
console.log(bar); // 1

ただ代入するには差分はありません。 次に前述のブロックスコープについて試してみます。

'use strict';

if (true) {
    var foo = 1;
    let bar = 1;
}

console.log(foo); // 1
console.log(bar); // ReferenceError: bar is not defined

varで宣言された変数はif文の中でブロックスコープを持ちません。 そのためif文の外からも呼び出すことが可能になっています。 それに対しletで宣言した変数はif文の中でブロックスコープが生まれるため、 if文の外から呼びだそうとするとerrorが返るようになります。

それではif文の中から親へのアクセスはできるのでしょうか。

'use strict';

let bar = 1;

if (true) {
    bar = 10;
}

console.log(bar); // 10

10が表示されていますね。 if文の中からでも親のブロックスコープ内で宣言されている変数にアクセスすることはできるみたいです。 ※if文で例を記載しておりますが、for文でも同様に動作します。

変数の再宣言

letとconstには注意しなければならない仕様があります。 それは変数を同一ブロックスコープ内で再定義できない点です。 varはこれを行うことができます。

'use strict';

var foo = 1;
var foo = 2; // 2

let bar = 1;
let bar = 2; // SyntaxError: Identifier 'bar' has already been declared

let hoge = 1;
hoge = 2; // 2

このようにletを同一変数内で再宣言するとエラーが発生します。 再代入はもちろん問題ありません。 このエラーは意図しない名前空間のバッティングを未然に防ぐ効果が期待できるため、非常に良い機能ということがわかります。

constを学ぶ

基本

続いてconstを解説します。
前述した通りconstは定数を宣言するものと説明させていただきました。 定数は読み取り専用の値として扱われるため、再宣言はもちろん再代入もできません。 つまりconstは宣言時から変わることがない値を代入する変数です。 また、ブロックスコープの範囲についてもletと同じになります。

const foo = 1;
const foo = 2; // SyntaxError
foo = 3; // TypeError

上記のように再宣言・再代入がともにエラーが返ってしまいできません。

constで宣言した配列とオブジェクトの振る舞い

constで宣言された配列やオブジェクトは、内部プロパティと値が変更される場合にはエラーが発生しません。 実際に見てみましょう。

const arr = [];
arr.push('hoge');
arr[1] = 'foo';
arr.push(1);
arr.pop();
console.log(arr); // ['hoge', 'foo']
const obj = {};
obj.foo = 'foo';
obj.bar = 'bar';
delete obj.bar;
console.log(obj); // Object{ foo: 'foo' }

このようにエラーが発生せず宣言した配列やオブジェクトの内部は通常通りに扱うことが可能です。

変数の巻き上げ

varで宣言した変数の場合、ブロックスコープ内で変数の巻き上げと呼ばれる仕様があります。 おさらいとして変数の巻き上げを見てみましょう。

function func() {
    console.log(str);
    var str = 'hoge'; // undefined エラーにはならない
}
func();

これが変数の巻き上げです。
変数が宣言される前に変数strを呼び出していますが、エラーにはなっていません。 上記のコードはjavascriptの振る舞いを考慮して書き直すと実際には以下のようになっています。

function func() {
    var str;
    console.log(str);
    str = 'hoge';
}
func();

ブロックスコープ内の一番最初にvar str;が宣言されています。 そして再代入するようにしてstr = 'hoge';が記載されています。 ブロックスコープ内で変数を有効にするためにまず先行して宣言される挙動を「変数の巻き上げ」と呼びます。

letとconstの場合、この巻き上げは発生しません。

function func() {
    console.log(num); // ReferenceError ただしbabelを使うとvarになるためエラーにならない
    let num = 10;
}
func();

このようにエラーが返ってきてしまいます。 そしてトランスパイラのbabelを使用した場合、ES5基準で書き直されるためletとconstはvarとして宣言されます。 そのため、上記コードはbabelを通すとエラーにならない点に注意してください。

letとconstのまとめ

こうしてletとconstを明確に使い分けることで、 コードのリーディングタイムを軽減することが可能になります。 宣言時に役割を明確にできるのは想像以上に便利で、これに慣れるとvarのみでのコーディングには戻ることが困難になるでしょう。 実際のコーディング時には基本的にはconstで宣言し、コード内で値が変わる要素のみletで宣言するのがよいみたいです。 ES6の中でも学習コストが低い部類かと思われますので、機会があればぜひ導入してみてください。