1. 程式人生 > >ES5中模仿塊級作用域

ES5中模仿塊級作用域

有一定JavaScript開發經驗的人應該會熟悉下面這種立即執行函式的寫法:

(function(name){
  console.log('hello ' + name);// hello Bob
})('Bob');

不過即使不熟悉也沒關係,這裡我會講解這種寫法的含義。

先來看下面這個更容易理解的示例:

var sayHello = function(name){
  console.log('hello ' + name);
}
var name = 'Bob';
sayHello(name);// hello Bob

首先使用函式表示式的語法定義函式sayHello,然後宣告一個變數name

,緊接著呼叫sayHello,引數為name

由於變數name沒有在其他地方被使用,這裡其實也就沒有必要宣告,可以使用更簡便的語法,直接把值傳遞給函式:

sayHello('Bob');// hello Bob

按照這種邏輯,假如sayHello也是隻執行一次,sayHello(name)不是也可以寫成下面這種形式嗎?

function(name){
  console.log('hello ' + name)
}('Bob');
//Uncaught SyntaxError: Unexpected token (

但是執行發現報錯了,這是因為JavaScript將function關鍵字當作一個函式宣告的開始,而函式宣告的後面是不能跟圓括號的。不過,函式表示式的後面可以跟圓括號,要將函式宣告轉換成函式表示式,只要像下面這樣給它加上一對圓括號即可:

(function(name){
  console.log('hello ' + name);// hello Bob
})('Bob');

也就是本文開頭的立即執行函式,模仿塊級作用域正是通過立即執行函式實現的。

來看下面的示例:

function outputNumbers(count){
  for(var i = 0; i < count; ++i){
    console.log(i);
  }
  console.log(i);// 5
}
outputNumbers(5);

在Java、C++等語言中,變數i只會在for迴圈的語句塊中有定義,迴圈一旦結束,變數i就會被銷燬。可是在JavaScript中,作用域只分為全域性作用域和函式作用域,函式內部宣告的變數是繫結在函式作用域對應的變數物件上的,也就是說這裡的i

是定義在函式的變數物件上的,在函式內部的任何位置都可以訪問它,即使for迴圈結束,i依然存在。

但是如果用立即執行函式包裹for迴圈,情況就不一樣了:

function outputNumbers(count){
  (function(){
    for(var i = 0; i < count; ++i){
      console.log(i);
    }  
  })();
  console.log(i);//Uncaught ReferenceError: i is not defined
}
outputNumbers(5);

此時i繫結在內部立即執行函式的變數物件上,立即執行函式執行完畢就會銷燬,i也會隨之銷燬。根據閉包機制,內部的立即執行函式也可以訪問到count。這樣就實現了塊級作用域——無論在立即執行函式中宣告什麼變數都不會影響外部變數的使用。

這種技術也經常在全域性作用域中被用在函式外部,從而限制向全域性作用域中新增過多的變數和函式,避免衝突,例如:

(function(){
  var now = new Date();
  if(now.getMonth() == 0 && now.getDate() == 1){
    console.log('Happy new year!');
  }
})();

這段程式碼放在任何全域性作用域中都可以在1月1日向用戶顯示祝賀新年的訊息,不必擔心全域性作用域中是否定義了now變數,從而引起變數