2015年12月10日 星期四

JavaScript 閉包介紹

閉包

閉包是JavaScript中最強大的特性之一。JavaScript允許函式嵌套,並且內部函式可以訪問定義在外部函式中的所有變數和函式,以及外部函式能訪問的所有變數和函式。但是,外部函式卻不能夠訪問定義在內部函式中的變數和函式。這給內部函式的變數提供了一定的安全性。而且,當內部函式生存週期大於外部函式時,由於內部函式可以訪問外部函式的作用域,定義在外部函式的變數和函式的生存週期就會大於外部函式本身。當內部函式以某一種方式被任何一個外部函式作用域訪問時,一個閉包就產生了。

var pet = function(name) {          // The outer function defines a variable called "name"
  var getName = function() {           
    // The inner function has access to the "name" variable of the outer function
    return name;
  }
  // Return the inner function, thereby exposing it to outer scopes
  return getName;              
},
myPet = pet("Vivie");
   
myPet();                            // Returns "Vivie"

實際上可能會比上面的程式碼複雜的多。在下面這種情形中,返回了一個包含可以操作外部函式的內部變數方法的物件。

var createPet = function(name) {
  var sex;
 
  return {
    setName: function(newName) {
      name = newName;
    },
   
    getName: function() {
      return name;
    },
   
    getSex: function() {
      return sex;
    },
   
    setSex: function(newSex) {
      if(typeof newSex == "string"
        && (newSex.toLowerCase() == "male" || newSex.toLowerCase() == "female")) {
        sex = newSex;
      }
    }
  }
}

var pet = createPet("Vivie");
pet.getName();                  // Vivie

pet.setName("Oliver");
pet.setSex("male");
pet.getSex();                   // male
pet.getName();                  // Oliver

在上面的程式碼中,外部函式的name變數對內嵌函式來說是可取得的,而除了通過內嵌函式本身,沒有其它任何方法可以取得內嵌的變數。內嵌函式的內嵌變數就像內嵌函式的保險櫃。它們會為內嵌函式保留「穩定」——而又安全——的資料參與執行。而這些內嵌函式甚至不會被分配給一個變數,或者不必一定要有名字。

var getCode = (function(){
  var secureCode = "0]Eal(eh&2";    // A code we do not want outsiders to be able to modify...
 
  return function () {
    return secureCode;
  };
})();

getCode();    // Returns the secret code

儘管有上述優點,使用閉包時仍然要小心避免一些陷阱。如果一個閉包的函式用外部函式的變數名定義了同樣的變數,那在外部函式域將再也無法指向該變數。

var createPet = function(name) {  // Outer function defines a variable called "name"
  return {
    setName: function(name) {    // Enclosed function also defines a variable called "name"
      name = name;               // ??? How do we access the "name" defined by the outer function ???
    }
  }
}

閉包中的神奇變數this是非常詭異的。使用它必須十分的小心,因為this指代什麼完全取決於函式在何處被呼叫,而不是在何處被定義。一篇絕妙而詳盡的關於閉包的文章可以在這裡找到。

沒有留言:

張貼留言