BETA

【要約-改訂新版JavaScript本格入門】関数について③

投稿日:2019-07-31
最終更新:2019-08-22

以下の続きである。
【要約-改訂新版JavaScript本格入門】関数について①
【要約-改訂新版JavaScript本格入門】関数について②

4.3 変数はどの場所から参照できるか -スコープ-

スコープとは「変数がスクリプトの中のどの場所から参照できるかを決める」概念。(=変数が使用できる有効範囲)
JavaScriptのスコープは以下2つに分類される。

  • グローバルスコープ
    スクリプト全体から参照できる
  • ローカルスコープ
    定義された関数内でのみ参照できる

※ES2015からは「ブロックスコープ」という概念が追加された。それについては4.3.6項で別途解説

4.3.1 グローバル変数とローカル変数の違い

関数の外で宣言した変数→グローバル変数
関数の中で宣言した変数→ローカル変数
※var命令で変数を定義しているという前提

var val = "Global Variable";  

function getVal() {  
    var val = "Local Variable";  
    return val;  
}  
console.log(getVal()); // Local Variable  
console.log(val); // Global Variable  

スコープが異なる場合、それぞれの変数は同名であっても別物として認識される。


4.3.2 変数宣言にvar命令が必須な理由

var命令を使わずに宣言された変数は、以下のようにすべてグローバル変数として扱われる。

val = "Global Variable";  

function getVal() {  
    val = "Local Variable";  
    return val;  
}  
console.log(getVal()); // Local Variable  
// getVal()が実行された時点でvalの値は上書きされる  
console.log(val); // Local Variable  

つまり、ローカル変数を定義するためには必ずvar命令を使用する必要がある。

「グローバル変数にはvar命令をつけずにローカル変数にはvar命令をつける」というのは混乱のもとになるため、原則としてvar命令は省略すべきではない。


4.3.3 ローカル変数の有効範囲はどこまで

変数の巻き上げについて

関数内で宣言されたローカル変数は、変数の巻き上げにより、その関数の先頭で宣言したものとみなされる。
ただし、巻き上げられるのは宣言部分のみであり、中身の代入が行われていないため、下記のコードのように意図しない結果を生み出す可能性がある。
防止策としてローカル変数は関数の先頭で宣言すること。

var val = "Global Variable";  

function getVal() {  
    // どんな値が出力されるか???  
    // 結果はグローバル変数valの値Global VariableやReferenceErrorではなく  
    // undefinedとなる  
    console.log(val);  

    var val = "Local Variable";  
    return val;  
}  
getVal();  


// ↓↓↓ つまり上記の文は以下と同じである。  
var val = "Global Variable";  

function getVal() {  
    var val;  
    console.log(val);  
    val = "Local Variable";  
    return val;  
}  
getVal();  


4.3.4 仮引数のスコープ -基本型と参照型の違いに注意する-

以下のように、仮引数は「基本的に」ローカル変数として処理される。

// グローバル変数  
var val = 10;  

// 仮引数valはローカル変数  
function decrementVal(val) {  
    return --val;  
}  
console.log(decrementVal(val)); // 9と出力される  
console.log(val); // グローバル変数は仮引数の影響を受けずに10と出力される  

ただし、参照型の値を仮引数として渡す場合は上記と挙動が変わる。

// グローバル変数  
var array = [1, 2, 3, 4, 5];  

// 配列の末尾の要素を削除する関数  
function pop(array) {  
    array.pop();  
    return array;  
}  
console.log(pop(array)); // [1, 2, 3, 4]と出力される  
console.log(array); // グローバル変数arrayはpop関数内での処理に影響を受けているため、[1, 2, 3, 4]と出力される  

なぜなら、参照型は値そのものではなく、値を格納したメモリ上のアドレスだけを格納しており、参照型の値を渡す場合にも渡される情報はメモリ上のアドレスとなるため。


4.3.5 ブロックレベルのスコープは存在しない(ES2015以前)

JavaやC#のような言語の場合、以下のコードではエラーが発生する。

// Javaコード  
if(true) {  
    int i = 5;  
}  
// 変数iはifブロック内でのみ有効なため、エラーが発生する  
System.out.println(i);  

しかし、JavaScriptでは、ブロックレベルのスコープは存在しないため、同様のコードは成立する。

if(true) {  
    var i = 5;  
}  

console.log(i); // 5  
即時関数で変数名の競合を防ぐ

関数によってスコープが決まるなら、関数をスコープの枠組みとして利用する方法。

(function() {  
    if(true) {  
        var i = 5;  
    }  
 }).call(this);  

// エラーになる  
console.log(i); // Uncaught ReferenceError: i is not defined  

アプリを作る場合、コード全体を即時関数でくくることで、アプリ以外のコード(例えばライブラリなど)と変数名が競合する心配がなくなる。


4.3.6 ブロックスコープに対応したlet命令

ES2015で追加されたlet命令は、ブロックスコープに対応した変数を宣言できる。
また、const命令で定義された定数も同様にブロックスコープを持つ。
「スコープはできる限り限定すべき」という一般的なプログラミングルールにのっとれば、ES2015が利用できる環境ではvar命令ではなくlet命令を使用すべき。

if(true) {  
    let i = 5;  
}  

// エラーになる  
console.log(i); // Uncaught ReferenceError: i is not defined  

let命令を用いた上記コードと前項で触れた即時関数を使用したコードは同じ結果が得られる。
即時関数よりもlet命令を使用した方がコードがシンプルになるため、ES2015環境ではlet命令を使用すること。


4.3.7 関数リテラル/Functionコンストラクタにおけるスコープの違い

関数の中で使用した場合、関数リテラルとFunctionコンストラクターでは、以下のようにスコープの解釈が異なる。

var val = "Global Variable";  

function check() {  
    var val = "Local Variable";  

    // 関数リテラル  
    // ローカル変数valを見ている  
    var getVal_1 = function() {return val;};  
    console.log(getVal_1()); // Local Variable  

    // Functionコンストラクタ  
    // グローバル変数valを見ている  
    var getVal_2 = new Function('return val;');  
    console.log(getVal_2()); // Global Variable  
}  

check();  

Functionコンストラクタ配下の変数は、その宣言場所にかかわらず、常にグローバルスコープとみなされる。
混乱を避けるため、前項4.1.2で記述した通り、原則Functionコンストラクターは使用しない方がよい。

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

funacchiの技術ブログ。

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう
目次をみる
技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
or 外部アカウントではじめる
10秒で技術ブログが作れます!