Javascript function scopes 和 closures

Javascript 用 function 來當作一個 scope. 在 function 裡面再宣告一個 function 的話就會產生一個新的 scope. 我們來看看下面的範例吧.

function outer_scope(){
  var a = 'I am `a` from outer scope',
      b = 'I am `b` from outer scope';

  console.log( 'logging from outer scope before inner scope function declaration' );
  console.log( 'a: ' + a );
  console.log( 'b: ' + b );
  console.log( '------------------------------------------' );

  function inner_scope_1(){
    console.log( 'logging from inside function inner_scope_1 before variable declaration' );
    console.log( 'a: ' + a );
    a = 'I will overwrite the outer scope `a`';
    console.log( 'logging from inside function inner_scope_1 after variable declaration' );
    console.log( 'a: ' + a );
    console.log( '------------------------------------------' );
  }

  function inner_scope_2(){
    console.log( 'logging from inside function inner_scope_2 before variable declaration' );
    console.log( 'b: ' + b );
    var b = 'I will not overwrite the outer scope `b`';
    console.log( 'logging from inside function inner_scope_2 after variable declaration' );
    console.log( 'b: ' + b );
    console.log( '------------------------------------------' );
  }

  inner_scope_1();
  inner_scope_2();

  a = 'I will be the new `a`';
  b = 'I will be the new `b`';

  console.log( 'logging from outer scope after inner scope executed' );
  console.log( 'a: ' + a );
  console.log( 'b: ' + b );
  console.log( '------------------------------------------' );
}

outer_scope();
結果
logging from outer scope before inner scope function declaration
a: I am `a` from outer scope
b: I am `b` from outer scope
------------------------------------------
logging from inside function inner_scope_1 before variable declaration
a: I am `a` from outer scope
logging from inside function inner_scope_1 after variable declaration
a: I will overwrite the outer scope `a`
------------------------------------------
logging from inside function inner_scope_2 before variable declaration
b: undefined
logging from inside function inner_scope_2 after variable declaration
b: I will not overwrite the outer scope `b`
------------------------------------------
logging from outer scope after inner scope executed
a: I will be the new `a`
b: I will be the new `b`
------------------------------------------

從上面的結果可以清楚的看到在內層 scope 沒有用 var 宣告的變數會複寫掉外層 scope 的變數. 在內層裡用 var 開頭宣告的變數會變成 local 變數並只存在這一個 scope( function ) 裡面.

這也被叫做 closure. 那我們又是在何時會用到以及如何使用 closure 呢? 在物件導向程式設計裡通常是用 class attributes 來儲存資料. 在 Javascript 用 closure 我們可以把資料存在上一層 scope 的變數裡以便跨 function 使用.

範例
function photo(){
  var name = 'ben';

  return{
    say_my_name : function(){
      console.log( name );
    },

    rename : function( new_name ){
      name = new_name;
    }
  };
}

var pic = new photo;

pic.say_my_name();

pic.rename( 'bibi' );

pic.say_my_name();
結果
ben
bibi

node.js function scope

node.js 有他處理 scope 的慣例. 在瀏覽器執行的 javascript, 在最上層 scope 用 var 宣告的變數就會是全域變數. 這就是為什麼我們常用 self executing anonymous function pattern 來避免全域函數.

在瀏覽器端的 javascript 下面的程式碼等於 window.name = 'ben'
var name = 'ben';
在 node.js 裡當它載入一個檔案或模組時會在外層包一層 executing anonymous function
var name = 'ben';

// the above equals to the following
( function( global ){
  var name = 'ben';
})( global );
要讓變數成全域變數你必須要用 global 這個 keyword 來宣告
global.name = 'ben';

希望上面的範例能讓你清楚的了解什麼是 scope 以及如何使用 closure. 了解了之後接下來我們要來看看 javascript callbacks.


Related posts