摘要:函數中的閉包閉包是指有權訪問另一個函數作用域中的變量的函數。理解閉包預與變量此時返回注意觀察下面的輸出內容,理解函數的調用時刻和把的賦值給變量時刻這個函數會返回長度為的函數數組。
Javascript函數中的閉包
閉包是指有權訪問另一個函數作用域中的變量的函數。創建閉包的常見方式就是,在一個函數的內部創建另一個函數。
有關創建作用域鏈以及作用域鏈有什么作用的細節對于徹底理解閉包至關重要。在作用域鏈中,外部函數的活動對象始終處于第二位,外部函數的外部函數的活動對象處于第三位,……直至作為作用域重點的全局環境。
圖1. compare函數作用域鏈示意圖
以上代碼先定義了compare函數,然后又在全局作用域中調用了它。當第一次調用時,會創建包含this、arguments、value1、value2的活動對象。全局執行環境的變量對象(包含result、 this、 compare)在compare()執行環境的作用域鏈中則處于第二位。
圖2.Compare函數執行時的作用域鏈
無論什么時候在函數訪問一個變量時,就會從作用域鏈中搜索具有相應名字的變量。一般來講,當函數執行完畢之后,局部活動對象(這里指compare函數創建的對象,包含this、arguments、value1、value2)就會被銷毀,內存中只保留全局作用域(全局執行環境的 變量)。但是,閉包的情況又有所不同:
在另一個函數的內部定義的函數會將包含函數(即外部函數)的活動對象添加到它的作用域鏈上。 var compareName=createComparationFunction("name"); var result=compare({name:"Nicho"},{name:"Greg"}); compareName=null;//解除對匿名函數的引用(以便釋放內存) 注:compareName沒有函數聲明Function,是匿名函數。
在匿名函數從createComparationFunction函數中被返回之后,它的作用域鏈被初始化為包含createComparationFunction函數的活動對象和全局作用對象。匿名函數可以訪問createComparationFunction函數中定義的所有變量。
更為重要的是:函數在執行完畢之后,其活動對象也不會銷毀,因為匿名函數的作用域鏈仍然在引用這個活動對象,直到匿名函數被銷毀之后,createComparationFunction函數的活動對象才會被銷毀。由于閉包包含它的函數的作用域,因此會比其他函數占用更多的內存。
This:匿名函數具有全局執行環境。
理解閉包預與變量: function createFunctions(){ var result=new Array(); for(var i=0;i<10;i++){ result[i]=function(){ return i; }; } //alert(result);此時返回[function (){return i;},...,function (){return i;}] return result; } var arr=createFunctions();
//注意觀察下面的輸出內容,理解函數的調用時刻和把i的賦值給變量時刻
alert(arr);//[function (){return i;},function (){return i;},...,function (){return i;}] alert(arr());//[10,10,……,10]
這個函數會返回長度為10的函數數組。
表面上看,似乎每個匿名函數都應該返回自己在數組result中的索引值,但實際上每個函數都返回10。因為每個函數的作用域鏈中都保存著函數的活動對象,所以他們引用的是同一個變量i,當函數返回后,變量i的值為10。
或許只是知道了這個原理還不能夠理解為什么每個函數都返回10,那么你就有可能要了解函數(這里指的是匿名函數)的執行時刻了:在上述函數中的匿名函數在定義函數體之后不會主動執行,所以此時的rusult返回了[function (){return i;},...,function (){return i;}] 數組里面包含10個函數表達式,而不是匿名函數表達式執行之后的返回值組成的數組;而arr()執行時,此時i的值是10,所以每個匿名函數表達式執行后都會返回10。
先來了解一下函數聲明、函數表達式和匿名函數
函數聲明:function fnName () {…};使用function關鍵字聲明一個函數,再指定一個函數名,叫函數聲明。
函數表達式:var fnName = function () {…};使用function關鍵字聲明一個函數,但未給函數命名,最后將匿名函數賦予一個變量,叫函數表達式,這是最常見的函數表達式語法形式。
匿名函數:function () {}; 使用function關鍵字聲明一個函數,但未給函數命名,所以叫匿名函數,匿名函數屬于函數表達式!函數有很多作用,賦予一個變量則創建函數,賦予一個事件則成為事件處理程序或創建閉包等等。
函數聲明和函數表達式不同之處在于:
Javascript引擎在解析javascript代碼時會‘函數聲明提升"(Function declaration Hoisting)當前執行環境(作用域)上的函數聲明,而函數表達式必須等到Javascirtp引擎執行到它所在行時,才會從上而下一行一行地解析函數表達式
而且表達式后面可以加括號立即調用該函數,函數聲明不可以,只能以fnName()形式調用
具體內容請查看有關函數的章節。
接下來我們來學習如何讓匿名函數立即執行,來達到預期的目的:
function createFunctions(){
var result=new Array(); for(var i=0;i<10;i++){ result[i]=( function(){ return i; }) (); } return result; } var arr=createFunctions(); alert(arr);//[0,1,2,……,9]
(function(){return i; }());可以讓匿名函數表達式立即執行,此時result[index]里面存的就是匿名函數立即執行后的返回值。
下面貼出立即調用的函數表達式的幾種方法(PS:雖然我只試過前三個):
1.最流行的寫法:(function(){// do something })(); 2.[function(){// do something }()]; 3.(function(){// do something }()); 4.!function(){ // do something }(); 5.~ function() {}(); 6.+ function() {}(); 7.- function() {}() 8.delete function() {}(); 9.typeof function() {}(); 10.void function() {}(); 11.new function() {}(); 12.new function() {}; 13.var f = function() {}(); 14.1, function() {}(); 15.1 ^ function() {}(); 16.1 > function() {}();
注:有些人的確會用「自執行的匿名函數」(self-executing anonymous function)這個術語,但是 Ben Alman 推薦了一個更準確的叫法:「立即調用的函數表達式」(IIFE,Immediately-Invoked Function Expression——雖然更長了點,但是這個術語,縮寫漂亮,含義更清晰。
function(){// do something }()會提示語法錯誤:
function(){}()并不是合法的EcmaScript字符串,因為 JavaScript 文法明確規定表達式語句不得以 function 或者 { 開頭。而“!function(){}()”是,們從規范出發。
閉包的用途:
閉包可以用在許多地方,它的最大用處有兩個:
一個是可以讀取函數內部的變量
另一個就是讓這些變量的值始終保持在內存中。
怎么來理解這句話呢?請看下面的代碼。
function f1(){ var n=999; nAdd=function(){n+=1} function f2(){ alert(n); } return f2; } var result=f1(); result(); // 999 nAdd(); result(); // 1000 在這段代碼中,result實際上就是閉包f2函數。它一共運行了兩次,第一次的值是999,第二次的值是1000。這證明了,函數f1中的局部變量n一直保存在內存中,并沒有在f1調用后被自動清除。
為什么會這樣呢?原因就在于f1是f2的父函數,而f2被賦給了一個全局變量result,這導致f2始終在內存中,而f2的存在依賴于f1,因此f1也始終在內存中,不會在調用結束后,被垃圾回收機制(garbage collection)回收。
這段代碼中另一個值得注意的地方,就是“nAdd=function(){n+=1}”這一行,首先在nAdd前面沒有使用var關鍵字,因此 nAdd是一個全局變量,而不是局部變量。其次,nAdd的值是一個匿名函數(anonymous function),而這個匿名函數本身也是一個閉包,所以nAdd相當于是一個setter,可以在函數外部對函數內部的局部變量進行操作。
使用閉包的好處:
希望一個變量長期駐扎在內存當中
避免全局變量的污染
私有成員的存在
閉包的用法:
模塊化代碼
在循環中直接找到對應元素的索引
注:函數嵌套函數,內部函數可以引用外部函數的參數和變量,參數和變量不會被垃圾回收機制所收回:
執行say667()后,say667()閉包內部變量會存在,而閉包內部函數的內部變量不會存在.使得Javascript的垃圾回收機制GC不會收回say667()所占用的資源,因為say667()的內部函數的執行需要依賴say667()中的變量。這是對閉包作用的非常直白的描述. function say667() { // Local variable that ends up within closure var num = 666; var sayAlert = function() { alert(num); } num++; return sayAlert; } var sayAlert = say667(); sayAlert()//執行結果應該彈出的667
問題求解:
function createFunctions(){
var result=new Array(); for(var i=0;i<10;i++){ result[i]= function(){ return i; }(); } return result; } var arr=createFunctions(); alert(arr);//[0,1,2,……,9]
我想立即調用匿名函數來給result[index]賦值,網上查閱有幾種方法是:
1.!function(){ // do something }(); 2.~ function() {}(); 3.+ function() {}(); 4.- function() {}()
有人說function(){return i; }();會提示語法錯誤
實際情況是function(){return i; }();并沒有提示錯誤。而+-!~會改變匿名函數返回值
回答:具體應該說是result= function(){//dosomething}();不會有語法錯誤吧,function() {//dosomething}();多帶帶執行還是有語法錯誤的。
result=
1.function(){//dosomething}();
2.!function(){ // do something }();
3.~ function() {}();
4.+ function() {}();
5.- function() {}()
原理是語句function(){//dosomething}()都被當作表達式處理了(賦值前計算表達式的值),就是2、3、4、5例可以自執行,自執行的原因也是function(){//dosomething}()由于前面的符號被當作表達式處理了。
至于result= function(){//dosomething}();不會有語法錯誤就是因為賦值語句把右邊的function(){//dosomething}()當作表達式處理了,然后將結果賦給result。
var someFunction=function(){ //這里是塊級及作用域 }; someFunction(); 這里定義了一個函數(匿名函數,并把值賦給someFunction)然后立即調用了它(在函數名稱后面添加一對括號)。 function(){ //這里是塊及作用域 }(); 這段代碼會導致語法錯誤,是因為javascript將function關鍵字當作一個函數聲明的開始,而函數聲明后面不能跟圓括號。而表達式后面可以跟圓括號,要將函數聲明變成表達式,只要向下面那樣給它加一對圓括號即可: (function(){ //這里是塊及作用域 })();
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/79445.html
摘要:局部變量,當定義該變量的函數調用結束時,該變量就會被垃圾回收機制回收而銷毀。如果在函數中不使用匿名函數創建閉包,而是通過引用一個外部函數,也不會出現循環引用的問題。 閉包是什么 在 JavaScript 中,閉包是一個讓人很難弄懂的概念。ECMAScript 中給閉包的定義是:閉包,指的是詞法表示包括不被計算的變量的函數,也就是說,函數可以使用函數之外定義的變量。 是不是看完這個定義感...
摘要:一般函數執行完畢,局部活動對象就會被銷毀,內存中僅僅保存全局作用域,但是閉包會長期駐扎在內存。我只是想通過這兩個例子來說明閉包的用處和好處。閉包會使變量始終保存在內存中,如果使用不當會增大內存消耗。 閉包特性 函數嵌套函數 函數內部可以引用外部的參數和變量 參數和變量不會被垃圾回收機制回收 閉包的作用 具體作用是有權訪問函數內部的變量,最常見的就是函數內部創建另一個函數,通過另一個函數...
閱讀 3733·2021-11-24 09:39
閱讀 2615·2019-08-30 15:54
閱讀 1156·2019-08-30 13:01
閱讀 3434·2019-08-28 18:30
閱讀 1627·2019-08-26 17:44
閱讀 3596·2019-08-26 11:31
閱讀 2419·2019-08-26 10:40
閱讀 1246·2019-08-26 10:27