グローバルスコープ
グローバルオブジェクト
グローバル変数やグローバルプロパティ、グローバルオブジェクトは、異なるスコープからでもアクセスすることができる。
JavaScriptにおいて、グローバルスコープで変数や関数を定義するとき、JavaScriptで元々定義されているwindowオブジェクトのプロパティやメソッドとして扱われる。
var myString = 'abc';
var myFunction = function(){};
console.log('myString' in window); //出力: true
console.log('myFunction' in window); //出力: true
グローバル関数
グローバル関数はどこからでも呼び出すことができる。
・decodeURI(encodeURI) - 引数として渡したURLエンコードされた文字列をデコードし、その文字列を返す。encodeURI()でエンコードされる文字のみがデコードされる。
・decodeURIComponent(encodeURI) - 引数として渡したURLエンコードされた文字列をデコードし、その文字列を返す。encodeURIComponent()でエンコードされる文字のみがデコードされる。
・encodeURI(uri) - 引数として渡した文字列にURLエンコードを行い、その文字列を返す。ただし、英数字といくつかの記号文字のエンコードは行わない。
・encodeURIComponent(uriComponent) - 引数として渡した文字列にURLエンコードを行い、その文字列を返す。ただし、英数字といくつかの記号文字のエンコードは行わない。
・eval(x) - 引数として渡した文字列をJavaScriptとして実行する。セキュリティホールとなりうるため、利用は推奨されない。
・isFinite(number) - 引数として渡した数値が実数かどうかを評価し、trueもしくはfalseを返す。引数には関数実行前にNumber()と同等の変換が行われる。
・isNaN(number) - 引数として渡した数値がNaNかどうか評価し、trueもしくはfalseを返す。引数には関数実行前にNumber()と同等の変換が行われる。そのため、実際には数ではないもの(true,null.'',' 'など)もfalseとして判定してしまう。
・parseFloat(string) - 引数として渡した文字列を数値に変換する。Number()とは文字列の変換方法が異なる。
・parseInt(string, radix) - strに渡した文字列を、radixに渡した底の値をもとに整数値に変換する。Number()とは文字列の変換方法が異なる。
グローバル変数とグローバルプロパティの違い
グローバル変数はvar演算子を使って定義されるもので、グローバルプロパティはvarを使わないで定義される。グローバルプロパティはdeleteで削除可能だがグローバル変数はdeleteで削除することができない。
確認用コード
var a = 10; //グローバル変数
b = 100; //グローバルプロパティ
delete a; //グローバル変数はdeleteで消去できない
delete b; //グローバルプロパティはここで削除される
console.log(a); //10
console.log(b); //bは削除されたのでエラーとなる
グローバル変数は関数外で定義するが、グローバルプロパティは、どのスコープからでも定義することができる。いいかえると、関数内でvarを使わずに定義したものはグローバルプロパティ扱いとなる。
var foo = function(){
var bar1 = 'a'; //関数内スコープ
bar2 = 'b'; //グローバルプロパティ
}();
try{
console.log(bar1);
} catch(e) {
console.log("Can't access bar1"); //関数内スコープの変数はアクセスできない
}
try{
console.log(bar2);
} catch(e) {
console.log("Can't access bar2"); //グローバルプロパティは異なるスコープでもアクセス可能
}
関数内で変数を定義するときは、必ずvarを使うべきである。
スコープチェーン(静的スコープ)とクロージャ
JavaScriptは変数を参照するときに、同じ関数内に目的の変数が定義されていないかを探す。見つからなかった場合、今度は親関数の中を探す。それでも見つからない場合はさらに親の関数内を探し、最終的にはグローバルスコープまで探しにいく。最後に見つからなかった場合は、undefinedを返す。
このように変数を参照することを、"スコープチェーンをたどる"という。スコープチェーンの検索は最初に発見した値を返す。したがって同じ名前をもった変数がスコープチェーンの上位にあっても、その変数が参照されることはない。(これをマスキングという)
クロージャとは「スコープチェーンに存在する変数への参照を保持している関数」である
クロージャの例を以下に示す
var countUpFromZero = function(){
var count = 0;
return function(){
return ++count;
};
}();
console.log(countUpFromZero());
console.log(countUpFromZero());
console.log(countUpFromZero());
クロージャは値ではなく参照を保持している。以下の例では、1 2 3と出力したいのだが失敗している。
var logElementNumber = function() {
var funcArray = [];
var i;
for(i = 0; i < 3; i++){
funcArray[i] = function(){ //クロージャ
console.log(i);
}
}
return funcArray;
}();
logElementNumber[0](); //出力 3 3回目のi++でiの値が3になる。クロージャのiは変わりゆくiを常に参照している。
logElementNumber[1](); //出力 3
logElementNumber[2](); //出力 3
クロージャを使わずに実装
var logElementNumber = function() {
var funcArray = [];
var func = function(n){
return function(){
console.log(n);
};
};
for (i = 0; i < 3; i++) {
funcArray[i] = func(i);
}
return funcArray;
}();
logElementNumber[0](); //出力 0
logElementNumber[1](); //出力 1
logElementNumber[2](); //出力 2
以下の例では、nは値が代入されているのに対しiは参照を渡しているのを確認することができる
var logElementNumber = function(len){
var funcArray = [];
var i;
var func = function(n) {
return function(){
console.log(n, i);//この場合は、nがこの関数のスコープでiが親関数のスコープ
};
};
for(i = 0; i < len; i++){
funcArray[i] = func(i);
}
return funcArray;
};//即時実行を行わない
var myArray1 = logElementNumber(3);
var myArray2 = logElementNumber(5);
myArray1[1]();//出力:1 3
myArray1[2]();//出力:2 3
myArray2[1]();//出力:1 5
myArray2[2]();//出力:2 5
ほかにjavascriptの仕様として重要なものがある。スコープチェーンは関数実行時ではなく関数定義時に決められるということだ。以下はグローバルスコープで宣言されているクロージャ関数だ。
var a = 1;
var func = function(){ console.log(a)};
(function(f){
var a = 100;
f();
})(func);
リファレンス
開眼!Javascript(第5章,第7章)