スコープ
javascriptのスコープは少々特殊である。
javascriptはブロックによるスコープを作らず、関数によってのみスコープを作る。
ブロックによるスコープを作らない例
for(var i = 0;i < 10;i++){
var sum = sum || 0;
sum += i;
}
console.log(sum); //45, エラーにならない
関数によってスコープが作られる例
for(var i = 0;i < 10;i++){
function add(){
var sum = sum || 0;
sum += i;
}
add();
}
console.log(sum); //エラー, ReferenceError: sum is not defined
クロージャ
クロージャは外のスコープの値への参照保持する。
var name = 'test';
var age = 20;
function myAlert(){
alert(name+', hello!' + ' age:'+age);
}
myAlert(); //test, hello! age:20 とアラートされる
注意することは"参照"を保持しているということである。
myAlert
は実行すると常に同じ文章を表示するわけではない。
例えば以下のようにmyAlert
を定義した後、name
,age
を変更すると表示内容が変わる。
var name = 'test';
var age = 20;
function myAlert(){
alert(name+', hello!' + ' age:'+age);
}
myAlert(); //test, hello! age:20 とアラートされる
name = 'test2';
age = 30;
myAlert(); //test2, hello! age:30 とアラートされる
この仕様は以下のような罠を含んでいる。
いつかあなたが絶対に引っかかる、ある一つのJavaScriptの罠 - Qiita
letステートメント
JavaScript 1.7ではlet
ステートメントを使用することでブロックスコープを実現できる。
for(var i = 0;i < 10;i++){
let sum = sum || 0; //ループの最初ではsumはundefinedのため常に0で初期化される
sum += i;
}
console.log(sum); //エラー, ReferenceError: sum is not defined
このlet
ステートメントを使用すると上記の記事にあるJavaScriptの罠を容易に解決できる。
//firefoxのコンソールなどで実行可能
for (var i = 0; i < 3; i++) {
let _i = i;
setTimeout(function() {
console.log(_i);
}, 0);
}
//0, 1, 2 とログがでる
まとめ
ブロックによるスコープをつくらないために変数名をよく考えてつける必要がある。
非同期処理でクロージャを使う場合は注意する。
JavaScript 1.7はlet
ステートメント以外にも便利な機能があるので早く使えるようになって欲しい。