1. 程式人生 > >JS閉包函式和回撥函式

JS閉包函式和回撥函式

一、閉包

閉包(closure)是Javascript語言的一個難點,也是它的特色,很多高階應用都要依靠閉包實現。閉包就是能夠讀取其他函式內部變數的函式。可以把閉包簡單理解成”定義在一個函式內部的函式”。
閉包有三個特性:
1.函式巢狀函式;
2.函式內部可以引用外部的引數和變數;
3.引數和變數不會被垃圾回收機制回收。
閉包是指有權訪問另一個函式作用域中的變數的函式,建立閉包的最常見的方式就是在一個函式內建立另一個函式,通過另一個函式訪問這個函式的區域性變數。使用閉包有一個優點,也是它的缺點,就是可以把區域性變數駐留在記憶體中,可以避免使用全域性變數。全域性變數在每個模組都可呼叫,這勢必將是災難性的。所以推薦使用私有的,封裝的區域性變數。一般函式執行完畢後,區域性活動物件就被銷燬,記憶體中僅僅儲存全域性作用域。但閉包的情況不同!
示例一:
//閉包就是一個函式的返回值為另外一個函式,在outer外部可以通過這個返回的函式訪問outer內的區域性變數.

function outer(){
     var val = 0;
     return function (){
           val += 1;
           document.write(val + "<br />");
     };
}
var outObj = outer();
outObj();//1,執行val += 1後,val還在
outObj();//2
outObj = null;//val 被回收
var outObj1 = outer();
outObj1();//1
outObj1();//2

閉包會使變數始終儲存在記憶體中,如果不當使用會增大記憶體消耗(如果上例中定義很多outer(),則記憶體中會儲存很多val變數)。
javascript的垃圾回收原理:
(1)、在javascript中,如果一個物件不再被引用,那麼這個物件就會被GC回收;
(2)、如果兩個物件互相引用,而不再被第3者所引用,那麼這兩個互相引用的物件也會被回收。
那麼使用閉包有什麼好處呢?使用閉包的好處是:


1.希望一個變數長期駐紮在記憶體中
2.避免全域性變數的汙染
3.私有成員的存在

二、回撥

百度百科:回撥函式就是一個通過函式指標呼叫的函式。如果你把函式的指標(地址)作為引數傳遞給另一個函式,當這個指標被用為呼叫它所指向的函式時,我們就說這是回撥函式。回撥函式不是由該函式的實現方直接呼叫,而是在特定的事件或條件發生時由另外的一方呼叫的,用於對該事件或條件進行響應。
在JavaScript中,回撥函式具體的定義為:函式A作為引數(函式引用)傳遞到另一個函式B中,並且這個函式B執行函式A。我們就說函式A叫做回撥函式。如果沒有名稱(函式表示式),就叫做匿名回撥函式。
回撥函式原理:我現在出發,到了通知你”。這是一個非同步的流程,“我出發”這個過程中(函式執行),“你”可以去做任何事,“到了”(函式執行完畢)“通知你”(回撥)進行之後的流程。
示例一:

function doSomething(callback){
     callback(1,2);
}
function numberAdd(a,b){
     document.write(a+b);
}
doSomething(numberAdd);//3

示例二:

function Thing(name){
     this.name = name;
}

//在Thing類里加入doSomething方法,這裡使用了構造器呼叫模式

Thing.prototype.doSomething = function(callback){
   callback(this.name);
};
function showName(name){
   document.write(name);
}
var t = new Thing("zhangsan");
t.doSomething(showName);//zhangsan

如果你有一個數字組成的陣列,你想寫個排序的公共方法,但是排序方式(從小到大或從大到小)是呼叫該排序方法的人決定。實現該排序方法可以用回撥來實現,當然你可以寫2個方法,一個是從小到大的排序,一個是從大到小的排序方法。回調個人認為就是將決定權交給了實際業務開發工程師,由他來決定怎麼去處理,這種思路跟我們平常接觸的不同,有點不習慣,但是這種思路在非同步程式設計中特別能看出它的好處,不知道我這麼理解是否正確。下面示例程式碼就是回撥的典型使用場合:

var arr = [25,13,33,8,23,32];
Array.prototype.sort = function(callback){
     var arr = this;
     var i = 0;//i在這裡定義與在for迴圈的括號內(for(var i = 0; i < ...))定義是一樣的
     for(; i < arr.length-1; i++){
          var j = i + 1;
          for(; j < arr.length;j++){
               if(callback(arr[i],arr[j])){
                    var temp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = temp;
               }
          }    
     }
return arr;
};
//a-b>0表示陣列從小到大排序
arr.sort(function(a,b){
     return a - b > 0;
});
document.write(arr.join(",") + "<br />");//8,13,23,25,32,33
//b-a>0表示陣列從大到小排序
arr.sort(function(a,b){
     return b - a > 0;
});
document.write(arr.join(","));//33,32,25,23,13,8

回撥函式的使用場合:
資源載入:動態載入js檔案後執行回撥,載入iframe後執行回撥,ajax操作回撥,圖片載入完成執行回撥,AJAX等等。
DOM事件及Node.js事件基於回撥機制(Node.js回撥可能會出現多層回撥巢狀的問題)。
setTimeout的延遲時間為0,這個hack經常被用到,settimeout呼叫的函式其實就是一個callback的體現
鏈式呼叫:鏈式呼叫的時候,在賦值器(setter)方法中(或者本身沒有返回值的方法中)很容易實現鏈式呼叫,而取值器(getter)相對來說不好實現鏈式呼叫,因為你需要取值器返回你需要的資料而不是this指標,如果要實現鏈式方法,可以用回撥函式來實現setTimeout、setInterval的函式呼叫得到其返回值。由於兩個函式都是非同步的,即:他們的呼叫時序和程式的主流程是相對獨立的,所以沒有辦法在主體裡面等待它們的返回值,它們被開啟的時候程式也不會停下來等待,否則也就失去了setTimeout及setInterval的意義了,所以用return已經沒有意義,只能使用callback。callback的意義在於將timer執行的結果通知給代理函式進行及時處理。

相關推薦

JS函式函式

一、閉包 閉包(closure)是Javascript語言的一個難點,也是它的特色,很多高階應用都要依靠閉包實現。閉包就是能夠讀取其他函式內部變數的函式。可以把閉包簡單理解成”定義在一個函式內部的函式”。 閉包有三個特性: 1.函式巢狀函式; 2.函式內

[js]利用向post函式傳引數

最近在閒逛校園XX站的時候,打算搞個破壞,試試有多少人還是用初始密碼登陸。比較懶,所以直接開啟控制檯來寫。   所以問題可以描述為:       向後端不斷的post資料,id從1~5000自增,後端會根據情況來返回值res,需要把res=100的id輸出。   

JavaScript中的引用函式、呼叫函式函式

引用函式與呼叫函式的區別 引用函式與呼叫函式的差別與函式名稱後是否附有括號()有關。函式引用只會單獨出現,但函式呼叫則必定後隨括號,很多時候還附有自變數。 舉個例子 // 函式引用 程式碼一 function f(){ var x = 5; retu

鉤子函式函式的區別?

鉤子的概念源於Windows的訊息處理機制,通過設定鉤子,應用程式可以對所有的訊息事件進行攔截,然後執行鉤子函式,對訊息進行想要的處理方式。 接下來是一段js程式碼,主要用於給btn設定點選的鉤子函式。鉤子是在捕獲訊息的時候立即執行鉤子函式 let btn = document.getEle

Go基礎系列:函式(2)——函式

回撥函式和閉包 當函式具備以下兩種特性的時候,就可以稱之為高階函式(high order functions): 函式可以作為另一個函式的引數(典型用法是回撥函式) 函式可以返回另一個函式,即讓另一個函式作為這個函式的返回值(典型用法是閉包) 一般來說,附帶的還具備一個特性:函式可以作為一個值賦值給變數

javascript分析函式

<div id="box1">First Box</div> <div id="box2">Second Box</div> <script> function animateIt(elementId) { var el

總結-全域性環境&執行流程&錯誤型別&垃圾回收機制&&函式&函式

全域性環境&執行流程&錯誤型別&垃圾回收機制&閉包&函式&回撥函式 全域性環境 1.函式中的this表示改函式所屬的物件 2.window物件的成員在呼叫時可以省略window 執行流程 1.編譯程式碼:當前script程式碼段

函式&函式

閉包函式&回撥函式 談到回撥函式,不得不提匿名函式;匿名函式,也叫閉包函式,也就是沒有名字的函式,它可以單獨存在,也可以將其賦值給某一個變數.so,先來看一下閉包函式。 閉包函式 php文件: 匿名函式(Anonymous functions),也叫閉包函式(closures),允許 臨

Nodejs學習筆記 day02——REPL函式

  1、REPL(互動式直譯器): Node自帶互動式直譯器: 讀取使用者輸入 ==> 執行輸入的資料結構 ==> 列印輸出結果(迴圈執行,直到使用者按ctrl+c兩下後結束)   變數: var x = 10 //宣告變數並賦值,如果沒有

JS之Callback function(函式

1.概念: 從概念上講,回撥函式與普通函式的本質在於:呼叫者的不同。普通函式由程式設計師程式碼呼叫,而回調函式由作業系統在適當的時間呼叫。   回撥函式主要用於處各種事件和處理。由於WINDOWS系統中存在大量程式設計師事先不可知的事件,例如滑鼠的單擊,程式設計師事先無法得知終

Python學習【第21篇】:程序池以及函式 python併發程式設計之多程序2-------------資料共享及程序池函式

python併發程式設計之多程序2-------------資料共享及程序池和回撥函式 一、資料共享 1.程序間的通訊應該儘量避免共享資料的方式 2.程序

js 中replace中的函式

定義和用法 replace() 方法用於在字串中用一些字元替換另一些字元,或替換一個與正則表示式匹配的子串。 我們常用的是:    使用replace進行字元替換,第二個引數傳入替換的引數: "

執行緒 -- 類函式 [轉]

由於在類中建立一個執行緒時要指定回撥函式的地址,這時候有兩種辦法:一是指定一個全域性的函式,二是指定類的static函式,但是如果我們想在static函式中訪問非static成員變數是個問題,可以如下來解決:   Thread.h #ifndef THREAD_H_H #def

函式指標,轉移表函式的理解

函式指標 函式指標顧名思義就是將函式看做一個指標,用一個指標來儲存函式的地址 函式指標的用法: 函式指標的正確寫法是  void (*p1)() 而 void *p2() 是無法存放函式指標的,因為這是返回值為指標的函式,p1先與*結合,說明p1是一個指標,指標指向一個

JS的運用及匿名函式的作用

轉載:https://blog.csdn.net/a250758092/article/details/52638209 配合上一篇js函式分類的部落格 1:閉包的目的 閉包的目的就是為了變數私有制,如果學過其他語言可以聯想到protected 這個關鍵詞,就是防止其

簡單用函式指標陣列函式實現計算器

利用函式指標陣列簡單實現計算器 函式指標陣列:以char *(*p[3])(char *)為例解釋,這是一個數組,陣列名為p,陣列記憶體儲了3個指向函式的指標 這些指標指向一些返回值型別為指向字元的指

【C++基礎之八】函式指標函式

C++很多類庫都喜歡用回撥函式,MFC中的定時器,訊息機制,hook機制等待,包括現在在研究的cocos2d-x中也有很多的回撥函式。1.回撥函式什麼是回撥函式呢?回撥函式其實就是一個通過函式指標呼叫的函式!假如你把A函式的指標當作引數傳給B函式,然後在B函式中通過A函式傳進

C語言實現動態陣列 C語言函式指標函式

實現任意資料型別的動態陣列的初始化,插入,刪除(按值刪除;按位置刪除),銷燬功能。、 動態陣列結構體   實現動態陣列結構體的三個要素:(1)陣列首地址;(2)陣列的大小;(3)當前陣列元素的個數。 1 //動態陣列結構體 2 struct DynamicArray{ 3 void **a

C#中委託、事件函式的理解

在C#中我們經常會碰到事件,尤其是在WPF或者WinForm中,窗體載入、或者點選一個按鈕,都會觸發事件。實際上,事件是對委託的封裝。如果不進行封裝,讓委託暴露給呼叫者,呼叫者就可以把委託變數重新引用到新的委託物件,也就刪除了當前要呼叫的方法列表;更糟糕的是,公共的委託成員打破了封裝不僅導致程式碼難以維護和除

如何取到js非同步函式函式裡的值?

chrome.cookies.get函式是非同步的,如果這麼寫: function getCookie(){ var result={}; chrome.cookies.get({'name':'prouserid','url':domain[0]}, f