関数がつくるスコープ -『JavaScript』

  • uto usui
  • //
table of contents

    スコープは変数がスクリプトの中のどの場所から参照できるかを決める概念です。JavaScriptのスコープは大きく分けると、

    • どこからでも参照できるグローバルスコープ
    • 定義された関数内でのみ参照できるローカルスコープ
    • ブロックスコープ《ES2015》

    この3つに分類されます。

    グローバル変数とローカル変数

    var命令なし、またはトップレベルで宣言された変数はグローバル変数とみなされグローバルスコープをもち、var命令で定義された変数はローカル変数になり、宣言された場所によって変数のスコープが決まるローカルスコープになります。


    var memberA = 'zoro'; // local function memberFunc () { var memberB = 'nami'; // local memberC = 'sanzi'; // global }

    ローカル変数を定義するには必ずvar命令を使用する必要があるので、関数内でグローバル変数を書き換える必要がある場合を除いては、変数はvar命令で宣言するということで考えます。

    ローカル変数の有効範囲

    ローカル変数とは宣言された関数全体で有効な変数のことです。しかし、関数内でも宣言前にその変数を参照してしまうと、undefined未定義になってしまいます。


    function memberFunc () { console.log('member'); // undefined var member = 'Zoro' }

    基本型と参照型の仮引数のスコープの違い

    仮引数は呼び出し先から関数に渡された値を受け取るための変数です。仮引数は基本形だとローカル変数として処理され、グローバルとは別物に扱われますが、参照型だと同じ位置を参照することになるため、グローバルとローカル変数が等しくなります。

    基本形型の仮引数

    基本型のローカル変数がグローバル変数に影響を与えることはありません。


    var n = 100; // 1 function calcTwice(n) { // 2 n * 2; return n; } console.log(calcTwice(200)); // 400 console.log(n); // 200 // 1と2の「n」は別物で値が違う

    参照型の仮引数

    参照型は値そのものでなく値を格納したメモリ上の位置を格納しているだけです。参照型の値を受け渡しすることを参照渡しといい、渡される値は位置の情報のみです。参照型のローカル変数を操作すると、グローバル変数は操作された状態と等しくなります。

    var members = ['zoro','sanzi','nami']; // 1
    function addMenber (members) { // 2
      members.push('momo');
      return members;
    }
    console.log(addMember(member)); // ['zoro','sanzi','nami','momo']
    console.log(member); // ['zoro','sanzi','nami','momo']
    
    // 1と2は別物だが、同じところを見ている
    
    

    ブロックレベルのスコープの代わりに即時関数

    変数の競合を防ぐためのシステムとして、JavaScriptは関数内にしかスコープは存在しないので、他のプログラミング言語にあるようなブロックスコープはありません。ここでのテクニックとしては即時関数を使うことで、擬似的にブロックスコープを表現します。


    (function(n) { var i = n; console.log('i:' + i); // i: 100 })(100); console.log('i:' + i); // error

    スクリプトを作成する際は、コードの一番外側を即時関数で囲っておくと他ライブラリなどから競合を防ぐことができます。

    ブロックスコープはlet命令で 《ES2015》

    let命令で変数を宣言すると、({ })のブロックスコープの範囲でのみ変数が有効になります。これでブロックスコープに対応した変数を宣言できます。

    if(true) {
      let n = 10;
    }
    console.log(n); // error
    

    letを利用するとブロックスコープになるので、前項で解説した即時関数は使わずにコードを簡潔に済ませる方がオススメです。

    関数リテラルとFunctionコンストラクタのスコープの違い

    関数リテラルとFunctionコンストラクタはスコープの解釈が異なります。Functionコンストラクタ配下にある変数は常にグローバルスコープとみなされます。

    var scope = 'global';
    
    function yourScope() {
      var scope  = 'local';
    
      var nami = function() {
        return scope;
      }
      console.log(nami()); // local
    
      var zoro = new Function('return scope;');
      console.log(zoro()); // global
    }
    yourScope();
    

    おわります。

    関数がつくるスコープ -『JavaScript』のアイキャッチ画像

    share

    related