「JavaScript/変数」の版間の差分

削除された内容 追加された内容
タグ: モバイル編集 モバイルウェブ編集
Ef3 (トーク | 投稿記録)
クリーンアップ
1 行
{{Nav}}
変数 ({{lang-en-short|variable}}) は、データオブジェクトやプリミティブに名前をつけることが、名前参照を可能にする機能です。
 
== 変数の宣言方法の全体像 ==
「変数」は、オブジェクトに名前をつけて参照の手助けをする機能です。
型や値は変数ではなくオブジェクトとプリミティブが保持しています<ref>オブジェクトとプリミティブをあわせてインスタンスということがあります。</ref>
静的な言語に慣れた人は、やや戸惑いを感じるかもしれません。
 
JavaScriptで変数を宣言する方法は、一般的には<code>var</code>, <code>let</code> または{{ <code|let}}>const</code> キーワードで宣言します。
 
== var ==
JavaScript の変数はES2015より前は <code>var</code> しか存在しませんでした。
'''コード例'''
<syntaxhighlight lang="javascript">
var x = 2;
documentconsole.writelog(x + 3); // x + 3 を計算評価した結果の 5 を表示
x = "abc";
console.log(x + 3); // x + 3 を評価した結果の abc3 を表示
</syntaxhighlight>
 
=== 未宣言のグローバル変数 ===
ですが、実は、なにもキーワードをつけず、
宣言を行う前に変数に代入することはできます。
<syntaxhighlight lang="javascript">
x = 2;
documentconsole.writelog(x + 3); // x + 3 を計算評価した結果の 5 を表示
</syntaxhighlight>
宣言を行っていない変数に値を代入することも可能で'''未宣言のグローバル変数'''を生成します。
 
ただし、未宣言のグローバル変数の生成は[[JavaScript/strictモード|strictモード]]では SyntaxError になることからも判る通り'''非推奨'''です。
のように、宣言を行っていない変数に値を代入することも可能です。
未宣言のグローバル変数の挙動は予測困難で難解なバグの原因になりえます。
 
このため、未宣言のグローバル変数はつくらないよう必ず <code>var</code>, {{code|let}} または {{code|const}} で宣言しましょう。
しかし、変数の宣言なしでの使用は非推奨です(書籍などでも紹介されない場合も多い)。
{{See also|[[JavaScript/関数|varの巻上げ]]}}
strictモードでは、宣言無しでの代入は SyntaxError になります。
 
可読性を低下させる意味でも、宣言無しでの代入は控えるべきです。
 
なので、変数の作成の際には var または let をつけましょう。また値を変える予定がないのであれば、後述の [[#定数|const]] の使用も検討して下さい。
 
var と let では、関数のブロックに対する振る舞いが違います(詳しくは [[#let|let の単元]]で後述します。)。
 
letが導入されたのはECMA2015(ES6)なので、それ以前のバージョンのJavaScriptではletは使えません。
 
少なくとも宣言なしで代入することに比べれば、var キーワードで宣言したほうが可読性も良いので宣言するように心がけましょう。
 
また宣言で初期値を与えないことも可能ですが、未初期化変数の参照は(意外なことに)エラーとはならないので、発見困難なバグの原因にしないためにも必ず宣言時に初期化を行いましょう。
 
<syntaxhighlight lang="javascript">
var x;
documentconsole.writelog(x); // undefined 表示(ReferenceErrorになるのは未宣言の場合で、この場合はならない)
y = 100; // 未宣言変数への代入
documentconsole.writelog(y); // 100 表示、結果的に代入が宣言のように振る舞う。
z = 200; // グローバル変数への代入の実体は、グローバルオブジェクトのプロパティへの代入
window.z = 200; // これとおなじ
</syntaxhighlight>
 
===代入===
既に var または let で作成された変数の値を書き換えるのならキーワード無しでも可能ですが、その場合は宣言ではなく代入になります。
<syntaxhighlight lang="javascript">
var x = 8;
documentconsole.writelog(x); // 8 表示
x = "abc"; // 違う型のオブジェクトを代入
documentconsole.writelog(x); // abc 表示
</syntaxhighlight>
代入をする時、それまでの値の型と違う型であっても構いません。
JavaScriptは動的な言語なので「変数はオブジェクトに名前を提供し参照の手助け」をしていることを端的に表しています。
 
{{code|const}} キーワードで定数を宣言する事が出来ます。JavaScriptにおける「定数」とは、その名の通り、値の変更を許さない変数のことです。(「定数」を「変数」の特殊なものに分類するのは、数学的には違和感を感じる人もいるかもしれないが、プログラミングでは便宜上、このように分類する。プログラミングでいう「変数」とは、単に値を格納するもの、という意味である。数学でいう、値の変動する代数とは、プログラミング「変数」は意味が異なることに、読者は留意してもらいたい。)
 
== 変数の自動判定 ==
JavaScriptでは、ブラウザなどの実行環境がコード中の文字を見て、それが数値か変数かを自動的に判定します。
 
たとえば下記のコードなら、
 
<syntaxhighlight lang="html5">
<script>
var x = 2;
// x + 3 を計算
document.write(x + 3);
</script>
</syntaxhighlight>
 
このようなコードなら、ブラウザが自動的に「2」は数値、xは変数だと判断します。(そもそも変数名に、数値だけからなる文字列は使えない。)
たとえば {{code|var 5 {{=}} 2;}} (「5」を変数として無理やりに使おうとしている。もちろんマチガイ)みたいな表記は禁止されている。
 
 
<!--
 
document.write はHTML5非推奨のため、廃止の可能性もあるので、
もし廃止された場合には下記の cosole.log のコードに差し替えのこと。
 
<SyntaxHighlight lang="JavaScript">
// x の 2 乗を計算
 
var x = 2;
 
console.log(x * x);
</SyntaxHighlight>
 
-->
 
いっぽう、「" "」をつけて
 
 
<syntaxhighlight lang="html5">
<script>
// x + 3を計算
var x = "2";
 
document.write(x + 3);
</script>
</syntaxhighlight>
 
とすると、今度は文字「2」のうしろに文字「3」を連結した結果「23」という表示になります。
 
 
上述のコードは、var キーワードでも、let キーワードでも、同様の結果になります。本節の以降の説明でも、varでもletでもどちらとも可能ですが、説明の単純化のため var に統一します。
 
 
なお、文字列型で宣言された数字の変数を、同じ内容の数値型に置き換えるメソッドがあります。
 
たとえば、数値計算をするには、たとえば整数どうしの計算に置き換えたいなら、下記のように <code> parseInt( )</code> を使い、
 
<syntaxhighlight lang="javascript">
var suuji = "2";
var kazu = 3;
 
document.write( parseInt(suuji) + kazu ); // 数値計算 2+3 の結果の「5」を表示
</syntaxhighlight>
<!--
<SyntaxHighlight lang="JavaScript">
var five = '5';
var one = 1;
 
console.log(five + one); // 文字列の「5」と数値の1を合わせ51
</SyntaxHighlight>
-->
 
のように書きます。これなら、文字列型で宣言された「2」を数値に置き換えるので、足し算 2+3 の結果の「5」が表示されます。
 
次のように複数の変数をまとめて宣言することもできます。
 
<syntaxhighlight lang="JavaScript">
var suuji = '5', kazu = 1; // 文字列の「5」と数値の1を宣言する
</syntaxhighlight>
 
ただし、これは他人から見ると予想外に見づらいので、1行に1つにすることを推奨します。もっともこれは面倒な<code>var</code>キーワードを省けるという意味はあります。
 
次のように宣言と代入を分けることは無意味で回りくどいので避けるのが良いでしょう(条件によって異なる値を代入したい場合などは除きます)。
 
<syntaxhighlight lang="JavaScript">
var suuji;
suuji = '5';
</syntaxhighlight>
 
また、宣言後また同じ変数で違う値を宣言すると変数は書き換えられます。
 
<syntaxhighlight lang="JavaScript">
var suuji = '5';
suuji = '50';
</syntaxhighlight>
 
宣言しただけで値を代入していない変数には、未定義であることを表す<code>undefined</code>が入っています。
 
<syntaxhighlight lang="JavaScript">
var suuji;
document.write(five); // undefined
</syntaxhighlight>
<!--
<SyntaxHighlight lang="JavaScript">
var five;
console.log(five); // undefined
</SyntaxHighlight>
-->
 
宣言も代入もされていない変数を参照しようとするとエラーになります。これは変数名をタイプミスした場合によく生じます。
 
<syntaxhighlight lang="JavaScript">
document.write(fibe); // ReferenceError: fibe is not defined
</syntaxhighlight>
<!--
<SyntaxHighlight lang="JavaScript">
console.log(fibe); // ReferenceError: fibe is not defined
</SyntaxHighlight>
-->
 
プログラムのトップレベル(プログラムの先頭など関数の内側ではない一番外側)で宣言された変数は、プログラムのどこからでもアクセスできる[[:w:グローバル変数|グローバル変数]]になります。また、<code>var</code>キーワードで宣言せずに変数に値を代入すると、トップレベル以外でもグローバル変数を定義することができます。<code>'use strict;'</code>はこのような明示的に宣言されていないグローバル変数の定義を禁止します。
 
<syntaxhighlight lang="JavaScript">
'use strict';
 
a = 3; // ReferenceError: assignment to undeclared variable a
</syntaxhighlight>
 
 
 
{{コラム|JavaScriptの数値の型|
C言語の数値変数には、整数型(int)や浮動小数型(float)などといった型があります。しかし JavaScript の数は倍精度の64ビットの浮動小数しかなく整数型は浮動小数の仮数部を使って表現されます。これはプリミティブな「数」の場合で、BigIntやTypedArrayは異なる内部構造を持っています。}}
 
== 変数名 ==
215 ⟶ 76行目:
Unicodeの文字の中に「いわゆる全角スペース」は含まれません。
 
== let ==
{{anchor|const}}
<code>var</code> キーワードは関数スコープを持つのに <code>if</code>文や<code>for</code>文などのスコープを持たないので不便な場合があったりJavaScriptに馴れないプログラマに混乱を引きおこす場合がありました。
 
ECMA2015から新たに導入された<code>let</code>キーワードで宣言された変数はブロックスコープをもちます。<code>{ }</code>の中がブロックで、ブロックを出ると<code>let</code>で宣言した変数は参照できなくなります。
== 定数 ==
定数(ていすう)は<code>const</code>キーワードで宣言します。
 
for文中の式は単文で<code>{ }</code> は使われていない場合も<code>for</code>文のスコープを持つと解釈されます。
数学において、定数(ていすう)とは変動しない値です。
 
つまり、<code>for</code>文の条件式でも<code>let</code>で宣言された変数は<code>for</code>文を出ると参照できなくなります。
JavaScriptにおける定数は宣言と同時な初期化が必須で、その後値は変更できません。
 
<syntaxhighlight lang="JavaScript">
'use strict';
 
for (let i = 0; i < 10; i++) // 波括弧{}でなくてもfor文はブロックスコープ
console.log(i);
console.log(i); // ReferenceError: i is not defined
</syntaxhighlight>
 
{{code|let}} は、ひとつのスコープ内での再宣言は出来ません。
<syntaxhighlight lang="javascript" line>
// 二行目でエラーになります
let x = 1; // これは代入ではなく初期化を伴うlet変数宣言
let x = 2; // 再宣言してるので SyntaxError: ここでスクリプトは中止
 
// x + 3 を計算
console.log(x + 3); // 既にエラーなので「5」は表示されない
</syntaxhighlight>
 
<code>let</code> 宣言された変数のインスタンスとの束縛を変えるには、<code>let</code>変数宣言ではなく代入を行います。
[[JavaScript/strictモード|strict モード]]でも代入が可能です。
 
つまり、下記のコードは表示が可能です。
<syntaxhighlight lang="javascript">
'use strict';
 
let x = 1; // 初期化を伴う let 変数宣言
 
x = 2; // 代入
// x + 3 を計算
console.log(x + 3); // 5 を表示
</syntaxhighlight>
 
当然、(strictモードではない)通常モードでも代入が可能です。
<syntaxhighlight lang="javascript">
// このコードは可能
let x = 1;
x = 2; // 代入
 
// x + 3 を計算
console.log(x + 3); // 5 を表示
</syntaxhighlight>
 
== const ==
<code>const</code>キーワードで変数を宣言すると、その値は初期化の時以外は書き換えられず「定数」(ていすう)として振る舞います。
<syntaxhighlight lang="javascript">
const E; // SyntaxError: Missing initializer in const declaration
</syntaxhighlight>
 
<code>const</code> で宣言された変数(定数)のスコープは <code>let</code> で宣言された変数と同じ'''ブロックスコープ'''を取ります。
定数はスコープの全域で使われる不変な値に対して用いられスコープの冒頭で宣言されるのが典型的な使われ方です。
 
<syntaxhighlight lang="javascript">
237 ⟶ 143行目:
</syntaxhighlight>
 
'''const の定数的でない例'''
定数名は全て大文字でアンダーバーで単語を区切り、<code>const CONSTANT_VALUES_LIKE_THIS</code>のようにスネークケースで書く慣習があります(ここで述べているのは習慣で、識別名として妥当であれば任意の変数名でconst変数宣言が出来ます)。
<syntaxhighlight lang="javascript">
const obj = { a: 1, b: 2};
 
for (const prop in obj)
console.log(prop);
/*
a
b
*/
</syntaxhighlight>
の様に、一度だけしか代入されない変数や for-in文 や for-of文 のループ変数も const で宣言することができ、値がブロックのかなで不変であったり左辺値化ができないことを明示することができます。「定数」と
 
constと宣言された定数への代入は SyntaxError を throw します。
246 ⟶ 163行目:
</syntaxhighlight>
 
<code>const</code> は定数を宣言しますが、Arrayオブジェクトや Objectオブジェクトのような複合的なオブジェクト(コレクション)の要素の変更は妨げません。<!--混乱するのでObject.seal() メソッドやObject.freeze()メソッドには触れなかった。-->
<syntaxhighlight lang="javascript" line>
const ary1 = [1,2,3];
257 ⟶ 174行目:
# 定数な筈のaryの要素を書き換え(ary1を初期化したインスタンスの要素の書き換え)。
# 書き換えたのは ary2 の1番めの要素なのに、ary1の要素が書き換わっている。これは、2行目でary2をary1で初期化しているためで、この時 ary2 は ary1 の別名になっています。
 
{{コラム|JavaScriptにおける「定数」とは値の変更を許さない変数のこと|
「定数」を「変数」の特殊なものに分類するのは、数学的には違和感を感じる人もいるかもしれないがプログラミングでは便宜上このように分類します。
 
プログラミングでいう「変数」とは、データに値を名前をつけるものという意味で、数学でいう値の変動する代数とはプログラミング「変数」は意味が異なることに気をつけて下さい。
}}
 
== 変数のスコープ ==
=== 基本 ===
変数が参照可能な範囲を変数の'''スコープ'''といいます。
 
=== 関数スコープ ===
<code>var</code>で宣言された変数はその関数外では参照ができなくなります。
この様なスコープを'''関数スコープ'''といいます。
281 ⟶ 204行目:
上記の''x''は関数の中と外では異なる変数になるので、外側の''x''の値は冒頭の<code>var x = 1;</code>のままです。
 
一方で、下記コードの''x''はブロックの中と外で同じ変数を指すので(var(<code>var</code>で宣言された変数は関数スコープを持ちます、しかし<code>if</code>スコープは持たないので)、''x''の値が変わってしまいます。
 
<syntaxhighlight lang="JavaScript">
293 ⟶ 216行目:
</syntaxhighlight>
 
なので実行結果は「22」
2
2
です。
 
これは次のような場合に問題となります。
1 秒間隔で 0, 1, 2 と出力するプログラムを書く場合、下記のようにコードを書いても、「333」となってしまい、失敗します。
<!-- 最後まで「1 秒間隔で 0, 1, 2 と出力するプログラム」にはなっていないと思う。Promise での実装を最後に書くべき? -->
 
<syntaxhighlight lang="JavaScript">
305 ⟶ 232行目:
}, 1000);
}
// -- しかし、実際に出力されるのは 「333」
</syntaxhighlight>
 
'''結果'''は
結果は 333 となり、 012 にはなってくれません。
3
3
3
となり、
0
1
2
にはなりません。
また、1秒ほど経過した後3行同時に表示する動きになります。
 
=== ブロックスコープ ===
なお、1秒ほど経過した後3文字同時に表示する動きになります。
2020年現在なら、この問題は<code>let</code>キーワードで容易に解決できます。ECMA2015で<code>let</code>キーワードが導入されました。
 
<code>{ }</code>の中がブロックで、ブロックの中で<code>let</code>で宣言した変数はブロックを出ると参照できなくなります。
2020年現在なら、この問題は<code>let</code>キーワードで容易に解決できます。ECMA2015でletキーワードが導入されました。
この様なスコープのことを'''ブロックスコープ'''と呼びます。
 
<syntaxhighlight lang="JavaScript">
321 ⟶ 257行目:
setTimeout(() => console.log(i), 1000);
}
 
// -- 実際の出力は「012」となり、成功。<!--成功?-->
</syntaxhighlight>
 
'''結果'''
0
1
2
 
なお、ECMAScript 5以前はlet宣言は無かったので、上述の問題を解決するには次のように即時関数(関数を定義すると同時に実行するためのイデオム)を使って関数スコープを作るしかありませんでした。
335 ⟶ 274行目:
</syntaxhighlight>
 
=== 発展的な知識 ===
'''1秒間隔でカウントダウンする例'''
<source lang="javascript">
cFunc(3);
 
== varの巻き上げ ==
function cFunc(n) {
<code>var</code> で宣言した変数は、未宣言で代入したあと改めて宣言すると、スコープの先頭にさかのぼって宣言されていたと仮定する(そのため宣言どころか代入前の参照が ReferenceError にならない)と言う挙動を示します。
if (n <= 0)
{{See also|[[JavaScript/関数|varの巻上げ]]}}
return;
console.log(n--);
setTimeout(() => cFunc(n), 1000);
}
</source>
関数定義の巻き上げを使い即時関数によらずキックスタートした。
どの道、コールバックでsetTimeout()の次のコールバックに自分自身への呼び出しをセットするのに名前は必要(いまではcalleeは非推奨な位置づけ)。
 
==== <code>var</code>の巻き上げ ====
『[[JavaScript/関数]]』で説明する。
 
==== その他 ====
{{コラム|console画面の開き方と使い方|
# ウェブブラウザを開く。
359 ⟶ 284行目:
# 画面下部などに出てきたウィンドウのタブ欄にあるconsoleタブをクリックして、console入力に切り替える。
# そこにコンソール用のコードを貼り付ける。
 
<code> console.log </code> でいう「log」とは、「記録をつける」などの意味のログです。
}}
 
==== let ====
===== 基本 =====
var キーワードは関数スコープを持つのに if文やfor文などのスコープを持たないので、不便な場合があったりJavaScriptに馴れないプログラマーに混乱を引きおこす場合がありました。
 
ECMA2015から新たに導入された<code>let</code>キーワードで宣言された変数はブロックスコープをもちます。関数ブロックに限らず、if文でもfor文でも、あるいは単独の<code>{ }</code> でも、letキーワードは必ずブロックスコープを持ちます。
 
for文中の式は、単文で<code>{ }</code> は使われていない場合もfor文のスコープを持つと解釈されます。
 
つまり、<code>for</code>文の条件式でも<code>let</code>で宣言された変数は、<code>for</code>文のブロックの内側でしか見えません。
 
<syntaxhighlight lang="JavaScript">
'use strict';
 
for (let i = 0; i < 10; i++) // 波括弧{}でなくてもfor文はブロックスコープ
console.log(i);
console.log(i); // ReferenceError: i is not defined
</syntaxhighlight>
 
===== let の特性 =====
{{code|let}} は、ひとつのスコープ内での再宣言は出来ません。
 
<syntaxhighlight lang="javascript">
// このコードはエラー
let x = 1; // これは代入ではなく初期化を伴うlet変数宣言
let x = 2; // 再宣言してるので SyntaxError: ここでスクリプトは中止
 
// x + 3 を計算
console.log(x + 3); // 既にエラーなので「5」は表示されない
</syntaxhighlight>
 
let 宣言された変数のインスタンスとの束縛を変えるには、let変数宣言ではなく代入を行います。
strict モードでも代入が可能です。
 
つまり、下記のコードは表示が可能です。
<syntaxhighlight lang="javascript">
'use strict';
 
let x = 1; // 初期化を伴う let 変数宣言
 
x = 2; // 代入
// x + 3 を計算
console.log(x + 3); // 5 と表示
</syntaxhighlight>
 
当然、(strictモードではない)通常モードでも代入が可能です。
<syntaxhighlight lang="javascript">
// このコードは可能
let x = 1;
x = 2; // 代入
 
// x + 3 を計算
console.log(x + 3); // 5 と表示
</syntaxhighlight>
 
== 脚注 ==
421 ⟶ 291行目:
 
== 参考文献 ==
* [https://mathiasbynens.be/notes/javascript-identifiers Valid JavaScript variable names in ECMAScript 5 - Mathias Bynens]
* [https://mathiasbynens.be/notes/es6-const ES6 const is not about immutability - Mathias Bynens]
 
[[Category:JavaScript|へんすう]]