JavaScriptでは変数を定義する際利用できるキーワードは三つあります。
- var:全バージョン利用可能
- const:ECMAScript 6以降から利用可能
- let:ECMAScript 6以降から利用可能
※ECMAScript 6は2015年6月に公開 各バージョンの公開日はここから参照
var
var文は関数スコープまたはグローバルスコープの変数を宣言し、任意でそれをある値に初期化します。
var message; //初期化していないため [undefined]という特殊な値が設定される
var message2 = "Hello World!";
function showMessage(){
console.info(message3);
var message3 = "ShowMessage";
}
function showMessage2(){
var message4;
console.info(message4);
message4 = "ShowMessage2";
}
showMessage(); //「undifined」が出力される
showMessage2(); //「undifined」が出力される
console.info(message); //宣言して、値を設定されていないから「undifined」が出力される
console.info(message2); //「Hello World!」が出力される
console.info(message3); // 宣言されていないため、例外になる
console.info(message4); // 宣言されていないため、例外になる
ちなみにvarなしで変数を宣言したら、グローバル変数になります
function show(){
message = "global var";
}
show();
console.info(message); //[global var]が出力される
※だがこのような変数宣言はお勧めしない、厳格モードの場合、エラーになります。
'use strict';
function show(){
message = "global var"; //未定義のエラー
}
show();
console.info(message);
巻き上げ(hoisting)
varでは同じスコープで同じ変数を複数回宣言してもエラーにはならない、かつ変数に新たな値をセットしない限り値も変わることはない。そしてスコープ内では宣言より前から利用可能です。これが出来る原因は巻き上げにあります。
console.log(hoisting); //undefined
var hoisting;
console.log(hoisting); //undefined
hoisting = 1;
console.log(hoisting); //1
var hoisting;
console.log(hoisting); //1
hoisting = 2;
console.log(hoisting); //2
巻き上げの際は変数の宣言をグローバル又は関数の先頭に移動したように見える。
var hoisting;
var hoisting;
console.log(hoisting); //undefined
console.log(hoisting); //undefined
hoisting = 1;
console.log(hoisting); //1
console.log(hoisting); //1
hoisting = 2;
console.log(hoisting); //2
気づいてないかもしれないが、関数にも巻き上げされるんです。以下のようなコードに見覚えがあると思います。
show(); //関数が定義される前から呼び出しが出来ます
function show(){
console.log("show message");
}
function show(){
console.log("show message");
}
show();
var tst = function(){};
var tst2 => ()
関数の巻き上げが変数より優先されます。
console.log(typeof show); // function
console.log(typeof tst1); // undefined
console.log(typeof tst2); // undefined
var show;
//宣言式の関数の巻き上げする
function show(){
console.log("show message");
}
var tst1 = function(){}; //関数式は巻き上げされない
var tst2 = ()=>{}; //アロー関数も巻き上げされない
let
letで宣言した変数のスコープはブロックスコープです。ブロックスコープは一番近い{}で決めるので、if、while、fuction、及び単なる{}でもlet変数のスコープになります。
if(true){
let a;
}
console.log(a); //ReferenceError: aが定義されていません
while(true){
let b;
break;
}
console.log(b); //ReferenceError: bが定義されていません
(function tst(){
let c;
})();
console.log(c); //ReferenceError: cが定義されていません varで宣言しても同じエラーになります
{
let d;
}
console.log(d);//ReferenceError: dが定義されていません
letはvarのように同じスコープで同じ変数を複数回宣言することはできません。
var a;
var a;
{
let b;
let b; //SyntaxError: b は既に宣言されている
}
letの動きはループ変数に非常に似合っている、varだとループがループの外部に漏れてしまうため。
for(var i=0;i<10;i++){}
console.log(i); // 10
for(let j=0;j<10;j++){}
console.log(j); // ReferenceError: jが定義されていません
厳密に言うとletで宣言した変数でも巻き上げされるのですが、一時的なデッドゾーン(temporal dead zone)によって変数が宣言される前に利用すると一律エラーになる。
const
constはletと基本的に同じ、正しいconstでは宣言と同時に初期化しなければならない、しかも初期化した後は変更不可
const message1; //例外
const message2 = "hello";
message2 = "hello2";//例外
const変数のパフォーマンスがvarとletより良いので、可能な場合はなるべくconstを使うように。
グローバル宣言
var firstName="first";
function tst(){
lastName = "last";
}
tst();
console.info(window.firstName);//first
console.info(window.lastName );//last
let age=20;
console.info(window.age);//[undifined]が出力される
letで宣言したグローバル変数はvarのようにwindowのプロパティにはならない。
思考
for(var i=0;i<5;i++){
setTimeout(()=>console.info(i),0);
}
for(var j=0;j<5;j++){
setTimeout((m)=>console.info(m),0,j);
}
for(let k=0;k<5;k++){
setTimeout(()=>console.info(k),0);
}