JavaScript/変数

出典: フリー教科書『ウィキブックス(Wikibooks)』
ナビゲーションに移動 検索に移動
はじめに JavaScript
変数
演算子

変数 (英: variable) は、データに名前をつけることができる機能です。

変数の宣言方法の全体像[編集]

「変数」は、オブジェクトに名前をつけて参照の手助けをする機能です。 型や値は変数ではなくオブジェクトが保持しています。 静的な言語に慣れた人は、やや戸惑いを感じるかもしれません。

JavaScriptで変数を宣言する方法は、一般的にはvarまたはlet キーワードで宣言します。

コード例

var x = 2;
document.write(x + 3); //  x + 3 を計算した結果の 5 を表示

ですが、実は、なにもキーワードをつけず、

x = 2;
document.write(x + 3); //  x + 3 を計算した結果の 5 を表示

のように、宣言を行っていない変数に値を代入することも可能です。

しかし、変数の宣言なしでの使用は非推奨です(書籍などでも紹介されない場合も多い)。 strictモードでは、宣言無しでの代入は SyntaxError になります。

可読性を低下させる意味でも、宣言無しでの代入は控えるべきです。

なので、変数の作成の際には var または let をつけましょう。また値を変える予定がないのであれば、後述の const の使用も検討して下さい。

var と let では、関数のブロックに対する振る舞いが違います(詳しくは let の単元で後述します。)。

letが導入されたのはECMA2015(ES6)なので、それ以前のバージョンのJavaScriptではletは使えません。

少なくとも宣言なしで代入することに比べれば、var キーワードで宣言したほうが可読性も良いので宣言するように心がけましょう。

また宣言で初期値を与えないことも可能ですが、未初期化変数の参照は(意外なことに)エラーとはならないので、発見困難なバグの原因にしないためにも必ず宣言時に初期化を行いましょう。

var x;
document.write(x); // undefined と表示(ReferenceErrorになるのは未宣言の場合で、この場合はならない)
y = 100; // 未宣言変数への代入
document.write(y); // 100 と表示、結果的に代入が宣言のように振る舞う。
z = 200; // グローバル変数への代入の実体は、グローバルオブジェクトのプロパティへの代入
window.z = 200; // これとおなじ

代入[編集]

既に var または let で作成された変数の値を書き換えるのならキーワード無しでも可能ですが、その場合は宣言ではなく代入になります。

var x = 8;
document.write(x); // 8 と表示
x = "abc"; // 違う型のオブジェクトを代入
document.write(x); // abc と表示

代入をする時、それまでの値の型と違う型であっても構いません。 JavaScriptは動的な言語なので「変数はオブジェクトに名前を提供し参照の手助け」をしていることを端的に表しています。

const キーワードで定数を宣言する事が出来ます。JavaScriptにおける「定数」とは、その名の通り、値の変更を許さない変数のことです。(「定数」を「変数」の特殊なものに分類するのは、数学的には違和感を感じる人もいるかもしれないが、プログラミングでは便宜上、このように分類する。プログラミングでいう「変数」とは、単に値を格納するもの、という意味である。数学でいう、値の変動する代数とは、プログラミング「変数」は意味が異なることに、読者は留意してもらいたい。)

変数の自動判定[編集]

JavaScriptでは、ブラウザなどの実行環境がコード中の文字を見て、それが数値か変数かを自動的に判定します。

たとえば下記のコードなら、

<script>
var x = 2;
// x + 3 を計算
document.write(x + 3);
</script>

このようなコードなら、ブラウザが自動的に「2」は数値、xは変数だと判断します。(そもそも変数名に、数値だけからなる文字列は使えない。) たとえば var 5 = 2; (「5」を変数として無理やりに使おうとしている。もちろんマチガイ)みたいな表記は禁止されている。


いっぽう、「" "」をつけて


<script>
// x + 3を計算
var x = "2";

document.write(x + 3);
</script>

とすると、今度は文字「2」のうしろに文字「3」を連結した結果「23」という表示になります。


上述のコードは、var キーワードでも、let キーワードでも、同様の結果になります。本節の以降の説明でも、varでもletでもどちらとも可能ですが、説明の単純化のため var に統一します。


なお、文字列型で宣言された数字の変数を、同じ内容の数値型に置き換えるメソッドがあります。

たとえば、数値計算をするには、たとえば整数どうしの計算に置き換えたいなら、下記のように parseInt( ) を使い、

var suuji = "2";
var kazu = 3;

document.write( parseInt(suuji) + kazu ); // 数値計算 2+3 の結果の「5」を表示

のように書きます。これなら、文字列型で宣言された「2」を数値に置き換えるので、足し算 2+3 の結果の「5」が表示されます。

次のように複数の変数をまとめて宣言することもできます。

var suuji = '5', kazu = 1; // 文字列の「5」と数値の1を宣言する

ただし、これは他人から見ると予想外に見づらいので、1行に1つにすることを推奨します。もっともこれは面倒なvarキーワードを省けるという意味はあります。

次のように宣言と代入を分けることは無意味で回りくどいので避けるのが良いでしょう(条件によって異なる値を代入したい場合などは除きます)。

var suuji;
suuji = '5';

また、宣言後また同じ変数で違う値を宣言すると変数は書き換えられます。

var suuji = '5';
suuji = '50';

宣言しただけで値を代入していない変数には、未定義であることを表すundefinedが入っています。

var suuji;
document.write(five); // undefined

宣言も代入もされていない変数を参照しようとするとエラーになります。これは変数名をタイプミスした場合によく生じます。

document.write(fibe); // ReferenceError: fibe is not defined

プログラムのトップレベル(プログラムの先頭など関数の内側ではない一番外側)で宣言された変数は、プログラムのどこからでもアクセスできるグローバル変数になります。また、varキーワードで宣言せずに変数に値を代入すると、トップレベル以外でもグローバル変数を定義することができます。'use strict;'はこのような明示的に宣言されていないグローバル変数の定義を禁止します。

'use strict';

a = 3; // ReferenceError: assignment to undeclared variable a


JavaScriptの数値の型

C言語の数値変数には、整数型(int)や浮動小数型(float)などといった型があります。しかし JavaScript の数は倍精度の64ビットの浮動小数しかなく整数型は浮動小数の仮数部を使って表現されます。これはプリミティブな「数」の場合で、BigIntやTypedArrayは異なる内部構造を持っています。

変数名[編集]

JavaScriptの変数名は、関数・プロパティ・ラベルなどとおなじく識別子(しきべつし;Identifier)の規約に従います。 JavaScriptの識別子は、Unicode の文字、$、_、数字(0-9)からなります。識別子は大文字小文字を区別します。 識別子は数字(0-9)からはじまってはいけません。 また予約語(よやくご;keyword)を識別子に使うことは出来ません。 変数名は関数名との重複は許されませんがラベル名との重複は許されます。

var primeNumber = 57; // OK
var prime_number = 57; // OK
var prime-number = 57; // SyntaxError、ハイフンは変数名に使えない

変数名にはJavaと同様、慣習的にvar variableNamesLikeThisのようなキャメルケースが用いられます。 外部に公開しない変数はあえて短めにし、一時的な変数であることを伝えようとする傾向がありますが、コーディング規約で定めていない限りリラックスした名付けが行われます。 ただし日本語の単語をローマ字表記した変数名は単数複数の区別がないので for (let item in items)の様な名付けが行えなかったり、のつもりで tuki と綴ったら ReferenceError: tsuki is not defined と翻字のゆらぎでミスタイプとなる可能性を増やすので推奨しません(この例ではtukiがmoonなのかmonthなのかにも曖昧さが生じ、いっそ var の方が良いのかもとさえ思えます)。

Unicodeの文字が有効なので

var π = 4 * Math.atan(1);

は正しいプログラムです。

Unicodeの文字の中に「いわゆる全角スペース」は含まれません。

const

定数[編集]

定数(ていすう)はconstキーワードで宣言します。

数学において、定数(ていすう)とは変動しない値です。

JavaScriptにおける定数は宣言と同時な初期化が必須で、その後値は変更できません。

const E; // SyntaxError: Missing initializer in const declaration

const で宣言された変数(定数)のスコープは let で宣言された変数と同じブロックスコープを取ります。 定数はスコープの全域で使われる不変な値に対して用いられ、スコープの冒頭で宣言されるのが典型的な使われ方です。

const E = 3;

console.log(E); // 3
E = 2.7; // TypeError: Assignment to constant variable.

定数名は全て大文字でアンダーバーで単語を区切り、const CONSTANT_VALUES_LIKE_THISのようにスネークケースで書く慣習があります(ここで述べているのは習慣で、識別名として妥当であれば任意の変数名でconst変数宣言が出来ます)。

constと宣言された定数への代入は SyntaxError を throw します。

const x = 12;
const y = x * 2; // 初期化には既知の値のみで組み立てられた式が使える
x = 1; // SyntaxError: Identifier 'x' has already been declared

const は定数を宣言しますが、Arrayオブジェクトや Objectオブジェクトのような複合的なオブジェクト(コレクション)の要素の変更は妨げません。

const ary1 = [1,2,3];
const ary2 = ary1;
ary2[1] = "abc";
console.log(ary); // 1,abc,3
  1. 変数 ary1 に配列インスタンスで初期化
  2. 変数 ary2 に ary1 を初期化したインスタンスで初期化
  3. 定数な筈のaryの要素を書き換え(ary1を初期化したインスタンスの要素の書き換え)。
  4. 書き換えたのは ary2 の1番めの要素なのに、ary1の要素が書き換わっている。これは、2行目でary2をary1で初期化しているためで、この時 ary2 は ary1 の別名になっています。

変数のスコープ[編集]

基本[編集]

変数が参照可能な範囲を変数のスコープといいます。

varで宣言された変数はその関数外では参照ができなくなります。 この様なスコープを関数スコープといいます。

まず、関数の場合のスコープのコード例を示します。

var x = 1;

(function() {
  var x = 2;
  console.log(x); // 2
})();

console.log(x); // 1

上記コードの実行結果は 21 です。プログラムは上から実行するので、上記コードの場合は結果的に最初に{}ブロック内の console.log(x); // 2 が実行され、次に{}ブロック外の console.log(x); // 1 が実行されるからです。

上記のxは関数の中と外では異なる変数になるので、外側のxの値は冒頭のvar x = 1;のままです。

一方で、下記コードのxはブロックの中と外で同じ変数を指すので(varは関数スコープを持つが、しかしif文のスコープは持たないので)、xの値が変わってしまいます。

var x = 1;

if (x === 1) {
  var x = 2;
  console.log(x); // 2
}
console.log(x); // 2

なので実行結果は「22」です。

これは次のような場合に問題となります。 1 秒間隔で 0, 1, 2 と出力するプログラムを書く場合、下記のようにコードを書いても、「333」となってしまい、失敗します。

// 1 秒間隔で 0, 1, 2 と出力したい
for (var i = 0; i < 3; i++) {
  setTimeout(function() {
    console.log(i);
  }, 1000);
}
// -- しかし、実際に出力されるのは 「333」

結果は 333 となり、 012 にはなってくれません。

なお、1秒ほど経過した後3文字同時に表示する動きになります。

2020年現在なら、この問題はletキーワードで容易に解決できます。ECMA2015でletキーワードが導入されました。

// 1 秒間隔で 0, 1, 2 と出力したい

// for の次が var i = 0 でなく let i = 0 に変わっている 
for (let i = 0; i < 3; i++) { // ECMA2015なのでアロー関数によるラムダ式を使った
  setTimeout(() => console.log(i), 1000);
}

// -- 実際の出力は「012」となり、成功。<!--成功?-->

なお、ECMAScript 5以前はlet宣言は無かったので、上述の問題を解決するには次のように即時関数(関数を定義すると同時に実行するためのイデオム)を使って関数スコープを作るしかありませんでした。

for (var i = 0; i < 3; i++) (function(i) {
  setTimeout(function() {
    console.log(i);
  }, 1000);
})(i);

発展的な知識[編集]

1秒間隔でカウントダウンする例

cFunc(3);

function cFunc(n) {
  if (n <= 0) 
    return;
  console.log(n--);
  setTimeout(() => cFunc(n), 1000);
}

関数定義の巻き上げを使い即時関数によらずキックスタートした。 どの道、コールバックでsetTimeout()の次のコールバックに自分自身への呼び出しをセットするのに名前は必要(いまではcalleeは非推奨な位置づけ)。

varの巻き上げ[編集]

JavaScript/関数』で説明する。

その他[編集]

console画面の開き方と使い方
  1. ウェブブラウザを開く。
  2. F12ボタンを押す。
  3. 画面下部などに出てきたウィンドウのタブ欄にあるconsoleタブをクリックして、console入力に切り替える。
  4. そこにコンソール用のコードを貼り付ける。

console.log でいう「log」とは、「記録をつける」などの意味のログです。

let[編集]

基本[編集]

var キーワードは関数スコープを持つのに if文やfor文などのスコープを持たないので、不便な場合があったりJavaScriptに馴れないプログラマーに混乱を引きおこす場合がありました。

ECMA2015から新たに導入されたletキーワードで宣言された変数はブロックスコープをもちます。関数ブロックに限らず、if文でもfor文でも、あるいは単独の{ } でも、letキーワードは必ずブロックスコープを持ちます。

for文中の式は、単文で{ } は使われていない場合もfor文のスコープを持つと解釈されます。

つまり、for文の条件式でもletで宣言された変数は、for文のブロックの内側でしか見えません。

'use strict';

for (let i = 0; i < 10; i++) // 波括弧{}でなくてもfor文はブロックスコープ
  console.log(i);
console.log(i); // ReferenceError: i is not defined
let の特性[編集]

let は、ひとつのスコープ内での再宣言は出来ません。

// このコードはエラー
let x = 1; // これは代入ではなく初期化を伴うlet変数宣言
let x = 2; // 再宣言してるので SyntaxError: ここでスクリプトは中止

// x + 3 を計算
console.log(x + 3); // 既にエラーなので「5」は表示されない

let 宣言された変数のインスタンスとの束縛を変えるには、let変数宣言ではなく代入を行います。 strict モードでも代入が可能です。

つまり、下記のコードは表示が可能です。

'use strict';

let x = 1; // 初期化を伴う let 変数宣言

x = 2; // 代入
// x + 3 を計算
console.log(x + 3); // 5 と表示

当然、(strictモードではない)通常モードでも代入が可能です。

// このコードは可能
let x = 1; 
x = 2; // 代入

// x + 3 を計算
console.log(x + 3); // 5 と表示

脚注[編集]


参考文献[編集]