2022/03/3(木) JSprimer(変数と宣言)

変数と宣言

プログラミング言語には、文字列や数値などのデータに名前をつけることで、繰り返し利用できるようにする変数という機能があります。

  • データを繰り返し利用できるようにする仕組みに変数があります。
  • JSでは、変数を宣言するキーワードとしてconst、let、varの3つがあります。
  • varは、昔からあった変数宣言のキーワードですが、意図しない挙動をしやすいという問題がありました。
  • それらの問題を改善するために作られたのが、ECMAScript 2015で導入されたconstletです。
  • varは、問題点もありますが、後方互換のために仕様を変更することはありません。その代わりに、constとletが追加されました。

[ES2015] const

  • const再代入できない変数を宣言するキーワードです。(全てが定数になる訳ではありません。)
  • そのため、constキーワードで宣言した変数に対して、後から値を代入しようとするとTypeErrorが発生します。
  • 変数に対して値を再代入する必要がない場合は、constキーワードで変数宣言するようにしましょう。
  • また、変数の宣言時に必ず初期値を入れるようにしましょう。
// const 変数名 = 初期値;(変数宣言)
const bookTitle = "JavaScript Primer";

// カンマを使って、複数の変数を同時に定義(複数の変数を宣言)
const bookTitle = "JavaScript Primer",
      bookCategory = "プログラミング";

// constで定義した変数に値を再代入しようとするとTypeErrorが発生!
const bookTitle = "JavaScript Primer";
bookTitle = "新しいタイトル"; // => TypeError: invalid assignment to const 'bookTitle'

[ES2015] let

  • constと同じくECMAScript 2015で導入されたletですが、値の再代入が可能な変数を宣言できます。
  • 変数宣言時に初期値を指定しない場合、デフォルト値としてundefined(未定義)という値で初期化されます。(constと異なり、変数宣言時に初期化が必須ではありません。)
// 変数宣言時に初期値を指定しない場合は、デフォルト値としてundefinedが代入されます。
// `bookTitle`は自動的に`undefined`という値になる
let bookTitle;

// constと異なり、値の再代入が何度でも可能です。
bookTitle = "JavaScript Primer";
bookTitle = "React";
bookTitle = "Ruby超入門";

var

  • varは、constとletの登場前からある変数宣言キーワードです。
  • varキーワードでは、値の再代入が可能な変数を宣言できます。
  • 変数宣言時に、初期値の代入が必須ではありません。
// varを使った場合でもletと同様に初期値がない変数を宣言できます。
// 変数宣言時に初期値を設定しない場合、let同様undefinedが代入されます。
var bookTitle;
console.log(bookTitle);  //=> undefined

// let同様、変数に対して値の再代入が可能です。
bookTitle = "JavaScript Primer";
bookTitle = "新しいタイトル";
console.log(bookTitle);  //=> 新しいタイトル

varの問題点①〜変数の再定義が可能〜

  • varキーワードには同じ名前の変数を再定義できてしまう問題があります。
  • letconstでは、同じ名前の変数を再定義しようとすると、SyntaxErrorが発生するため同じ変数名で2重に定義することが出来ません。
// "x"という変数名で変数を定義する
let x;
// 同じ変数名の変数"x"を定義するとSyntaxErrorとなる
let x; // => SyntaxError: redeclaration of let x

// yという変数を定義する
const y = 1;

// 定義済みの変数yを再定義する 
const y = 2; //=> => SyntaxError: Identifier 'y' has already been declared
  • しかし、varを使って同じ変数名で定義してもエラーとならずに、値を上書きしてしまいます。
  • これは、意図せずに値を書き換えてしまう挙動につながる可能性があるため、良いことではありません。
// "x"という変数を定義する
var x = 1;
// 同じ変数名の変数"x"を定義できる
var x = 2;
// 変数xは2となる
  • 現在、varは、[ES2015] で導入されたconstletに書き換えが可能になっています。varを使った変数宣言は、使用しないようにしましょう。

varの問題点②〜varによる変数の巻き上げの問題〜

  • varを使うことによる変数の巻き上げという問題があります。
  • 簡単に言うと、varで宣言された変数が宣言前に参照でき、その値がundefinedとなる特殊な動きをすると言うものです。具体例を以下で見ていきましょう。
// var宣言より前に参照してもエラーにならない
console.log(x); // => undefined(定義のみ最初に実行されるため、代入する値"varのx"は出力されません。)
var x = "varのx";
  • 通常の流れとしては、変数を参照する時、変数をまず宣言してから値を代入します。その後代入された値を変数を使って参照することになると思います。
  • そのため、宣言をする前に値の参照はできません。
  • しかし、varを使うと、記述の位置に関わらず、変数の宣言部分だけが関数やグローバルスコープの先頭に移動します。
  • 結果、値未代入の変数定義だけが先に行われてしまうため、undefinedが参照できるという仕組みです。
  • 上記のコードは実際の実行時には、次のように解釈されて実行されています。
// 解釈されたコード
// スコープの先頭に宣言部分が巻き上げられる
var x;
console.log(x); // => undefined
// 変数への代入はそのままの位置に残る
x = "varのx";
console.log(x); // => "varのx"

変数の宣言部分がもっとも近い関数またはグローバルスコープの先頭に移動しているように見える動作のことを変数の巻き上げ(hoisting)と呼びます。

varはlet、constとは異なった動作をしています。 varは巻き上げによりブロックスコープを無視して、宣言部分を自動的に関数スコープの先頭に移動するという予測しにくい問題を持っています。

変数名に使える名前のルール

  1. 半角のアルファベット、_(アンダースコア)、$(ダラー)、数字を組み合わせた名前にする
  2. 変数名は数字から開始できない
  3. 予約語と被る名前は利用できない
  • 変数名が数字から始まっていたり、数字のみで構成されている場合はSyntaxError: Invalid or unexpected tokenエラーになります。
var 1st; // NG: 数字から始まっている(SyntaxError: Invalid or unexpected tokenエラー)
var 123; // NG: 数字のみで構成されている(SyntaxError: Invalid or unexpected tokenエラー)
  • 予約語(構文として意味を持つキーワード)を変数名として使用することは出来ません。こちらもエラーが発生します。SyntaxError: Unexpected token '予約語'
var var; // NG: `var`は変数宣言のために予約されているので利用できない(SyntaxError: Unexpected token 'var')
var if; // NG: `if`はif文のために予約されているので利用できない(SyntaxError: Unexpected token 'var')

constは必ずしも定数ではない

  • constは「再代入できない変数」を定義する変数宣言ですが、必ずしも定数を定義するわけではありません。
  • 定数とは、一度定義した名前(変数名)が常に同じ値を示すものです。
  • 数値や文字列などのプリミティブな値をconstで定義した場合は、値の変更ができないため定数として定義できます。
  • プリミティブな値とは、値の変更できない文字列、数値、BigInt、真偽値などのことです。
  • 対して、オブジェクト、配列、関数などの値は、値を変更できます。
// TEN_NUMBERという変数は常に10という値を示す
const TEN_NUMBER = 10;
  • しかし、オブジェクト、配列、関数などをconst宣言した場合は、オブジェクトという値そのものは、初期化したあとでも変更できてしまうため、厳密に定数とは言えなくなってしまいます。
  • イメージとしては、オブジェクトという入れ物をconstで宣言します。しかし、オブジェクトという入れ物に入っている値は出し入れ自由ということです。
// `const`でオブジェクトを定義している
const object = {
    key: "値"
};
// オブジェクトそのものは変更できてしまう
object.key = "新しい値";

JavaScript において、プリミティブ (primitive、プリミティブ値、プリミティブデータ型) はオブジェクトでなく、メソッドを持たないデータのことです。 6 種類のプリミティブデータ型があります。文字列、数値、BigInt、真偽値、undefined、そしてシンボル (ECMAScript 2016 で追加) です。

すべてのプリミティブ値は、イミュータブルimmutable、つまり変更できません。変数には新しい値を再割り当てすることができますが、既存の値については、オブジェクト、配列、関数が変更できるのに対して、プリミティブ値は変更することができません。

Primitive (プリミティブ)

参照

JSPrimer

Primitive (プリミティブ)