一、定義函數:前端
(1)函數聲明數組
function 函數名(){}//指定函數名的方式閉包
它的一個重要特徵是函數聲明提高,在執行代碼以前會先讀取函數聲明。函數
(2) 函數表達式this
var FunctionName = function(arg0, arg1, arg2){}//匿名函數spa
在使用以前必須先賦值。指針
二、閉包code
閉包是指有權訪問另外一個函數做用域中的變量的函數。建立閉包的常見方式,就是在一個函數內部建立另外一個函數。對象
每一個函數都有本身的執行環境。每一個執行環境都有一個與之相關聯的變量對象,環境中定義的全部變量和函數都保存在這個對象中。當執行流進入到一個函數時,函數的環境會被推入一個環境棧中,而在函數執行以後,棧將其環境彈出,把控制權返回給以前的執行環境。blog
若是這個環境是函數,則將其活動對象做爲變量對象。
在建立某個函數時,會建立一個預先包含全局變量對象的做用域鏈,這個做用域鏈被保存在內部的[[Scope]]屬性中。當調用函數時,會爲函數建立一個執行環境,而後經過複製函數的[[Scope]]屬性中的對象構建起執行環境的做用域鏈。此後,又有一個活動對象(在此做爲變量對象使用)被建立並推入執行環境做用域鏈的前端。做用域鏈本質上是一個指向變量對象的指針列表,它只引用但不實際包含變量對象。
示例1:
function compare(value1, value2){ if(value1 > value2){ return 1; }else if(value1 < value2){ return -1; }else{ return 0; } } compare(2, 10);
在另外一個函數內部定義的函數會將包含外部函數的活動對象添加到它的做用域鏈中。
示例2:
function createComparisionFunction(propertyName){ return function(object1, object2){ var value1 = object1[propertyName]; var value2 = object2[propertyName]; if(value1 > value2){ return 1; }else if(value1 < value2){ return -1; }else{ return 0; } }; } //建立函數。匿名函數被返回後,它的做用域鏈被初始化爲包含createComparisionFunction()函數的活動對象和全局變量對象 var compareNames = createComparisionFunction('name'); //調用函數。createComparisionFunction()函數執行完畢後,其活動對象也不會被銷燬,由於匿名函數的做用域鏈仍然在引用這個活動對象 var result = compareNames({name: 'Tom'}, {name: 'Lucy'}); //解除對匿名函數的引用,以便釋放內存。匿名函數被銷燬後,createComparisionFunction()的活動對象纔會被銷燬 compareNames = null;
因爲閉包會攜帶包含它的函數的做用域,所以會比其餘函數佔用更多的內存,建議只在絕對必要時再考慮使用閉包。
三、閉包與變量
做用域鏈這種機制引出來一個反作用,即閉包只能取得包含函數中任何變量的最後一個值。
function createFunction(){ var result = new Array(); for(var i = 0; i < 10; i++){ result[i] = function(){ return i; } } return result; } //createFunction()函數返回一個函數數組。函數返回後,變量i的值是10,此時每一個函數都引用着保存變量i的同一個變量對象,因此每一個函數內部i的值都是10。 function createFunction(){ var result = new Array(); for(var i = 0; i < 10; i++){ result[i] = function(num){//定義匿名函數 return function(){//閉包 return num; }; }(i); } return result; }
//重寫後的createFunction(),每一個函數就會返回各自不一樣的索引值了。這個版本中,定義了一個匿名函數,並將當即執行該匿名函數的結果賦值給數組。在調用每一個匿名函數時,出入變量i,由於函數參數是按值傳遞的,因此會將變量i當前的值複製給參數num,在這個匿名函數內部,建立並返回了一個訪問num的閉包。
四、關於this對象
匿名函數的執行環境具備全局性,所以其this對象一般指向window。
每一個函數在調用時,其活動對象都會自動取得this和arguments這兩個特殊變量。內部函數在搜索這兩個變量時,只會搜索到其活動對象爲止,所以永遠不可能直接訪問外部函數中的這兩個變量。
var name = 'The window'; var object = { name:'myproject', getnameFunc:function(){ var that = this; return function(){ return that.name; }; } }; var getname = object.getnameFunc(); console.log(getname());//‘myproject’