javascript閉包以及閉包的作用

來源:互聯網
上載者:User

標籤:out   變數覆蓋   jquery   ima   cache   log   不同的   數列   分享圖片   

什麼是閉包?———>是一個函數,一個可以訪問其他函數內部資料的函數。

栗子一:

function foo() {    var a = 1;}function fn() {    console.log(a);//報錯,因為這裡是無法訪問到a的}
function foo() {    var a = 1;    function fn() {        console.log(a);//這樣是可以訪問到a的    }    return fn;//fn就是閉包,其在foo內部調用沒有意義,所以將其返回,交由外部來決定調用時機,更具開發意義,當執行外部函數時,才會建立閉包fn}

一、閉包基本結構:1.定義外層函數;2.定義內建函式;3.內層函數引用外層函數定義的資料;4.要將內層函數作為外層函數的傳回值; 
function outer() {    var data = {name: "xiaoming”};//外層函數的內部資料會一直緩衝在記憶體中    function inner() {        return data;    }    return inner;}var closure1 = outer();//拿到閉包之後就可以決定什麼時候執行它;console.log(closure1());var closure2 = outer();console.log(closure2 == closure1);//false,由於調用了兩次outer函數,從而建立了兩個data對象,因此兩個閉包訪問到的資料(data)在記憶體中的地址是不同的;var d1 = closure1();Var d2 = closure1();Console.log(d1 == d2);//true

一個閉包是共用一個資料的,兩個閉包則是兩個資料,所以不必建立兩個閉包(即執行兩次外部函數);

閉包實際應用: 點擊每個li列印出每個li對應的數字

 

<ul>    <li>0</li>    <li>1</li>    <li>2</li>    <li>3</li>    <li>4</li></ul><script>    var lists = document.getElementsByTagName("li");    var i = 0,        l = lists.length;    for (; i<l; i++) {        lists[i].onclick = function() {//給每個li綁定了點擊事件            console.log(i);//結果都列印5,因為當點擊li的時候,i已經變成了5        }    }</script>

 

運用閉包來解決問題:

var lists = document.getElementsByTagName("li");var i = 0,    l = lists.length;for (; i<l; i++) {    lists[i].onclick = (function(i) {//2.接收參數到外層函數內部,並作為內部資料緩衝在記憶體中        function fn() {            console.log(i);//3.這裡的i為外層函數的內部資料,分別為0,1,2,3,4        }        return fn;//4.返回到外部作為點擊事件的處理函數,當發生點擊事件時,觸發這個閉包的執行,那時列印出來的i為外層函數緩衝的i值    })(i);//1.自執行函數,將全域變數i依次傳入外層函數中}
 iife(立即執行函數運算式):(Function(){})();!function(){}();+function(){}();

 

二、閉包的作用1.避免全域汙染
var $ = function() {};//定義的事全域變數$//但如果在這之前引入了一個jquery.js,那麼jquery的$函數就會被這個全域變數$覆蓋
在日常開發中,這種事情會很常見,因為你不能保證其他開發人員會定義怎樣的變數,這時一個頁面先引入了的js檔案中的變數就有可能被後面的js檔案中定義的同名的全域變數覆蓋;另外,全域變數生命週期是隨著頁面的存在而存在的,頁面在,變數就會一直佔用記憶體,耗費效能,所以不推薦過多的使用全域變數;

 

function outer() {//外層函數中的$和attr其實就相當於全域變數,只要閉包存在,這些變數就會一直在    var $ = function() {};    var attr = 10;    return {//這裡面的$和attr不會被任何其他的全域變數汙染        $ : $,        getAttr : function() {            return attr;        }    }}var query = outer();query.$(“divs”);console.log(query.getAttr);

以上栗子中將$和attr變數放在一個外層函數裡面作為內部資料,而將閉包(指$和getAttr兩個方法)返回並賦值給一個全域變數query,當全域變數較多時就能夠極大的降低全域汙染的機率。

 

2.快取資料1)可用全域變數做緩衝———>生命週期太長耗費效能2)可用cookie\localStorage等做緩衝——>io流沒有在記憶體中訪問速度快3)用閉包來做緩衝 舉個栗子:求fibonancci數列第n項值 
function fib(n) {    if (n < 1) {        throw new Error("value not increct");    }    if (n == 1 || n == 2) {        return 1;    } else {        return fib(n - 1) + fib(n - 2);    }}

  遞迴方式求Fib(6)值的過程圖解:

                                                    這裡面有太多的重複計算,當要求的數值很大時,效能就會特別低;用閉包來進行最佳化: 
function createFib() {    var cache = [, 1, 1];//儲存計算結果,計算過的結果得以緩衝在記憶體中以便下次直接使用    return function(n) {//閉包這個函數的作用是求得地n項的值,閉包函數可拿到緩衝中已經計算過結果的那些項的值;        var res = cache[n];//如果緩衝中有這一項,那麼直接用        if (!res) {//若沒有這一項的值,就要重新計算            res = cache[n] = fib(n - 1) + fib(n - 2);//第n項的值為他的前兩項值的和,依舊用遞迴,這裡不同的是,計算過的值不用再重新計算,而是直接拿緩衝中的結果,另外本次計算完成後,也要將結果儲存到cache中以便下次使用,並且將值賦給res變數用來返回;        }        return res;    };}var fib = createFib();//需要遞迴的是fib這個閉包而不是外層函數createFib,因為閉包才是真正的執行求第n項值的功能函數,而外層函數的作用是用內部資料來做緩衝;console.log(fib(50));//大大提高計算效率

  閉包的方式計算fib(6)值的過程如下:

                                                          3、高階函數滿足以下條件之一就是高階函數
  • 函數類型作為參數(比如es5中forEach, map,filter,回呼函數)
  • 返回函數(閉包)
舉個栗子:求員工工資(底薪+提成),普通員工底薪1000,提成每個人都不一樣,經理底薪2000,提成每個經理都不同;  
function calSalary(base, ext) {    var base = base * 10 + 20;//每次計算都要重複    return base + ext;}var s1 = calSalary(1000, 100);var s2 = calSalary(1000, 200);::var p1 = calSalary(2000, 100);var p2 = calSalary(2000, 300);

  像這種有基數的計算,可以用閉包來進行最佳化:

function calSalary(base) {    var base = base * 10 + 20;//一次計算後作為緩衝    return function(ext) {        return base + ext;//這裡直接拿緩衝中的base,省去了很多重複計算    }}var s = calSalary(1000);//拿到閉包var s1 = s(500);//每次只執行閉包var s2 = s(1000);var p = calSalary(2000);//建立另一個閉包,因為共用資料不同var p1 = p(500);//同樣每次只執行閉包var p2 = p(500);

4、回呼函數傳參:栗子:給定時器的回呼函數傳參,實現div1秒後背景變成紅色; 
setTimeout((function(color){//立即執行函數,接收color參數,緩衝在記憶體中    var div = document.getElementById("div");    return function() {//返回一個函數作為延時回呼函數,1秒後執行        div.style.background = color;    }})(‘red‘), 1000)    

 

擴充:
  • setInterval和setTimeout第三個參數可向函數中傳參:setTimeout(function(){}, 200, 10);
  • bind方法可實現向回呼函數傳參 
  • setTimeout(function(color){    var div = document.getElementById("div”);    div.style.background = color;}.bind(null, ‘red‘), 1000)    

     

 

javascript閉包以及閉包的作用

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.