いまさら聞けない、ES2015で変わったJavaScriptの変数宣言の違い

2017/01/16

Julian Motz

56

Articles in this issue reproduced from SitePoint
Copyright © 2017, All rights reserved. SitePoint Pty Ltd. www.sitepoint.com. Translation copyright © 2017, KADOKAWA ASCII Research Laboratories, Inc. Japanese syndication rights arranged with SitePoint Pty Ltd, Collingwood, Victoria,Australia through Tuttle-Mori Agency, Inc., Tokyo

JavaScriptの変数の使い方と、ECMAScript 2015(ES6)で変わった点をコンパクトにおさらい。

JavaScriptを学ぶ基本の1つに、変数の使い方があります。変数は、number(数値)、string(文字列)、array(配列)など、あらゆる型(データ型を参照)を含む入れ物です。変数は、あとでアプリケーション内で使用する(たとえば、値の参照)ために名前が付けられます。

本記事では、変数の使い方と、宣言による違いを紹介します。

変数の宣言と初期化、代入の違い

変数の宣言の違いに入る前に、変数のライフサイクルを見ていきます。

Variable lifecycle flowchart

  1. 宣言(Declaration):変数は付けられた名前で適切なスコープ(関数の中など)に登録される段階
  2. 初期化(Initialization):変数は宣言されたとき、自動的に初期化され、JavaScriptのエンジンによってメモリが確保される段階
  3. 代入(Assignment):変数に特定の値が代入される段階

宣言の型

varは、JavaScriptがリリースされたときからずっと使われてきました。ES6(ES2015)から、新たにletconstが使えるようになりました。ブラウザーの互換性はこちらで確認してください。

var

構文:

var x; // Declaration and initialization
x = "Hello World"; // Assignment

// Or all in one
var y = "Hello World";

ECMAScript 6まで代替案がなかったため、おそらくこの宣言が一番使われています。varで宣言された変数のスコープは、宣言された関数内になります。特定の関数内で宣言されない変数はグローバルになります。

例:

function sayHello(){
  var hello = "Hello World";
  return hello;
}
console.log(hello);

例を実行するとReferenceError: hello is not definedというエラーが表示されます。helloという変数が、sayHelloという関数内だけで有効だからです。しかし、次のようにすれば動作します。変数がconsole.log(hello)と同じスコープでグローバルに定義されているからです。

var hello = "Hello World";
function sayHello(){
  return hello;
}
console.log(hello);

let

構文:

let x; // Declaration and initialization
x = "Hello World"; // Assignment

// Or all in one
let y = "Hello World";

letはモダンなJavaScriptにおいて、varの後継です。letのスコープは、関数内だけでなく、ブロック文内に制限されます。ブロック文は常に{}で囲われます(たとえば、if文やループ文など)。letの良いところは、変数のスコープが小さくなるため、エラーになる可能性を減らせることです。

例:

var name = "Peter";
if(name === "Peter"){
  let hello = "Hello Peter";
} else {
  let hello = "Hi";
}
console.log(hello);

例を実行するとReferenceError: hello is not definedというエラーが表示されます。helloという関数がブロック内(例だとif文内)でのみ有効だからです。しかし、次のようにすれば動作します。

var name = "Peter";
if(name === "Peter"){
  let hello = "Hello Peter";
  console.log(hello);
} else {
  let hello = "Hi";
  console.log(hello);
}

const

構文:

const x = "Hello World";

正確に言うと、定数は変数ではありません。定数の特徴は、宣言時に値を代入すると、再度代入できないことです。constletと同様に、スコープはブロック内です。

定数は、アプリケーションが動いているあいだはずっと、値の変更をしてはいけません。そのため、上書きしようとするとエラーが発生します。

もう1つのグローバル変数の作り方

いままで紹介した宣言は、関数の外で宣言するとグローバルになりますが、もし変数の前にvarletconstを書き忘れると、関数内で宣言しても自動的にグローバルになります。

例:

function sayHello(){
  hello = "Hello World";
  return hello;
}
sayHello();
console.log(hello);

例を実行すると、コンソールにHello Worldと表示されます。代入式hello =の前に変数の宣言がなく、変数がグローバルになっているからです。

メモ:誤ってグローバル変数にすることを防ぐには厳格モードが役に立ちます。

巻き上げによる一時的デッドゾーン

varletconstとほかの違いは、変数の巻き上げに関することです。変数の宣言は、いつもそのスコープの先頭に巻き上げられます。以下に例を紹介します。

console.log(hello);
var hello;
hello = "I'm a variable";

この例は、以下と同様の意味になります。

var hello;
console.log(hello);
hello = "I'm a variable";

上の2例を実行すると、コンソールにundefinedと表示されます。もし、先頭にvar hello;がなければReferenceErrorが表示されます。

この巻き上げによる挙動は、varだけではなくletconstでも起こります。先に述べたように、宣言されていないvar変数にアクセスしようとすると、JavaScriptが初期化時に代入するundefinedが値として返されます。

しかし、letconst変数は宣言前にアクセスするとエラーが発生します。この2つの変数は宣言の前にアクセスできないということです。変数のスコープ内で、変数が宣言される前までの区間を一時的デッドゾーン(変数にアクセスできない期間)と呼びます。

巻き上げについては、JavaScriptの変数スコープと巻き上げの解説を参考にしてください。

最後に

エラーになる確率を下げるためには、できるだけletconstを使うべきです。もし本当にvarを使う必要があるなら、スコープ内の1番上で宣言をして、巻き上げによる予期しないエラーを防ぎましょう。

※本記事はMark BrownMev-Raelが査読を担当しています。最高のコンテンツに仕上げるために尽力してくれたSitePointの査読担当者のみなさんに感謝します。

(原文:Quick Tip: How to Declare Variables in JavaScript

[翻訳:萩原伸悟/編集:Livit

Copyright © 2017, Julian Motz All Rights Reserved.

Julian Motz

Julian Motz

ソフトウェア開発者・メディアデザイナーで、現在はドイツのベルリンに住んでいます。Web・ハイブリッドアプリケーションに力を入れていて、オープンソース開発の愛好者です。

Loading...