日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

【javascript基础】8、闭包

發(fā)布時間:2025/4/5 javascript 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【javascript基础】8、闭包 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

函數(shù)和作用域啥的我們前面已經(jīng)了解了,現(xiàn)在就要學習閉包了,這是一個挺晦澀的知識點,初學者可能會感覺不好理解,但是高手都不不以為然了,高手就給我提點意見吧,我和新手一起來學習什么是閉包。

例子

先不說定義,先看一個題,看看大家能得出正確的結(jié)果不,

function test(){var arr = [];for(var i = 0;i<10;i++){arr[i] = function(){return i;}}return arr; }var fns = test(); console.log(fns[9]()); // 值是多少? console.log(fns[0]());//值是多少?

結(jié)果就是

10 10 View Code

你做對了嗎?

?

什么是閉包

我們知道,javascript中的變量作用域分為全局變量和局部變量,全局的變量我們在什么地方都可以使用,但是局部變量就不是這樣的了,我們只能在該變量的作用域中得到,換句話說就是我們在函數(shù)的內(nèi)部可以使用函數(shù)外部的變量,但是我們在函數(shù)的外部卻不能使用函數(shù)內(nèi)部定義的局部變量,但是在實際中我們就是想要在函數(shù)的外部使用函數(shù)內(nèi)部定義的變量那該怎么辦呢?例子來了

function test(){var inner = 10; } alert(inner);//error?咋辦

咋辦呢?我們知道,在內(nèi)部我們可以訪問到這個變量,我們還知道有一個操作符return可以返回想要的值,那我就在內(nèi)部定義一個函數(shù)來訪問這個變量,然后在返回這個函數(shù)不就行了,實踐一下

function test(){var inner = 10;function inFun(){alert(inner);// };return inFun; } var outter = test(); outter();//10;

我們做到了,為自己鼓鼓掌,有時候我們就該不斷鼓勵自己一下,不要給自己太大的壓力,我們不是富二代,在不鼓勵一下自己怎么能成為富二代他爹呢。

這就是閉包了,官方?jīng)]有給出閉包一個完整的準確的定義,民間流傳的是在一個函數(shù)內(nèi)定義一個函數(shù),并且這個內(nèi)部函數(shù)可以在外面訪問,這時候就形成了閉包??纯瓷厦婧瘮?shù)的結(jié)構(gòu),一個函數(shù)返回了一個內(nèi)部函數(shù),我們知道在正常情況下,一個函數(shù)執(zhí)行結(jié)束之后,里面的變量會被釋放,也就是說,在test()這句執(zhí)行之后,里面的inner應(yīng)該被釋放了才對,但是我們發(fā)現(xiàn),outter()時我們拿到了inner的值,這就是閉包的特性:如果閉包中使用了局部的變量,那么這個變量會一直貯存在內(nèi)存中,閉包會一直保持這個值,一直到外部的函數(shù)沒有被引用為止,看例子

function closure(){var num = 0;function add(){console.log(++num);}return add; } var test1 = closure();//形成一個閉包,保持著自己的一個num變量 test1 ();//1 test1 ();//2 var test2 = closure();//又一個閉包,保持了一個自己的num變量 test2 ();//1 test2 ();//2

好玩不?這就是閉包的神奇的地方,也是讓身為初學者的我們感到彷徨的地方,相信我,我會讓你們理解明白的。要想釋放num占用的內(nèi)存,就該這樣

test1 = null; test2 = null;

簡單解析下這個例子:在執(zhí)行?var test1 = closure()時,由于closure()返回到是一個函數(shù),這里就相當于test1變量指向了一個函數(shù)add,但是這個add函數(shù)有自己的作用域和活動對象,都存在了test1中,執(zhí)行test1()時,會尋找num變量,由于閉包存儲了該變量就可以直接取到,并且自加1,再一次執(zhí)行test1()時會繼續(xù)在test1執(zhí)行的add函數(shù)的執(zhí)行環(huán)境和作用域中查找,發(fā)現(xiàn)num為1了,就找到了這個num;在執(zhí)行var test2 = closure()時,會重新創(chuàng)建一個閉包,重新存儲執(zhí)行環(huán)境和活動對象,所以這是和第一次完全沒有關(guān)系的。

閉包的機制

  • 函數(shù)也是對象,有[[scope]]屬性(只能通過JavaScript引擎訪問),指向函數(shù)定義時的執(zhí)行環(huán)境上下文。

  • 假如A是全局的函數(shù),B是A的內(nèi)部函數(shù)。執(zhí)行A函數(shù)時,當前執(zhí)行環(huán)境的上下文指向一個作用域鏈。作用域鏈的第一個對象是當前函數(shù)的活動對象(this、參數(shù)、局部變量),第二個對象是全局window。

  • 當執(zhí)行代碼運行到B定義地方, 設(shè)置函數(shù)B的[[scope]]屬性指向執(zhí)行環(huán)境的上下文作用域鏈。

  • 執(zhí)行A函數(shù)完畢后,若內(nèi)部函數(shù)B的引用沒外暴,A函數(shù)活動對象將被Js垃圾回收處理;反之,則維持,形成閉包。

  • 調(diào)用函數(shù)B時,JavaScript引擎將當前執(zhí)行環(huán)境入棧,生成新的執(zhí)行環(huán)境,新的執(zhí)行環(huán)境的上下文指向一個作用域鏈,由當前活動對象+函數(shù)B的[[scope]]組成,鏈的第一個對象是當前函數(shù)的活動對象(this、參數(shù)、局部變量組成),第二個活動對象是A函數(shù)產(chǎn)生的,第三個window。

  • B函數(shù)里面訪問一個變量,要進行標志符解析(JavaScript原型也有標識符解析),它從當前上下文指向的作用域鏈的第一個對象開始查找,找不到就查找第二個對象,直到找到相關(guān)值就立即返回,如果還沒找到,報undefined錯誤。

  • 當有關(guān)A函數(shù)的外暴的內(nèi)部引用全部被消除時,A的活動對象才被銷毀。

  • 這段是其他的地方的,就是說了執(zhí)行環(huán)境和作用域的理解閉包怎么維持變量的。

    閉包的應(yīng)用

    一個是前面提到的可以讀取函數(shù)內(nèi)部的變量,另一個就是讓這些變量的值始終保持在內(nèi)存中,這既是函數(shù)也是弊端。我們可以利用閉包封裝一些私有的屬性,例如

    var factorial = (function () {var cache = [];return function (num) {if (!cache[num]) {if (num == 0) {cache[num] = 1;}cache[num] = num * factorial(num - 1);}return cache[num];} })();

    封裝了一個內(nèi)部私有的屬性來緩存結(jié)果。

    下面流行的模塊模式,它允許你模擬公共,私有以及特權(quán)成員

    var Module = (function(){var privateProperty = 'foo';function privateMethod(args){//do something }return {publicProperty: "",publicMethod: function(args){//do something },privilegedMethod: function(args){privateMethod(args);}} })();

    另一個類型的閉包叫做立即執(zhí)行函數(shù)表達式,是一個在window上下文中自我調(diào)用的匿名函數(shù):

    (function(window){var a = 'foo';function private(){// do something }window.Module = {public: function(){// do something }};})(this);

    ?

    閉包的弊端

    由于閉包會使得函數(shù)中的變量都被保存在內(nèi)存中,內(nèi)存消耗很大,所以不能濫用閉包,否則會造成網(wǎng)頁的性能問題,在IE中可能導(dǎo)致內(nèi)存泄露。解決方法是,在退出函數(shù)之前,將不使用的局部變量全部刪除。閉包會在父函數(shù)外部,改變父函數(shù)內(nèi)部變量的值。所以,如果你把父函數(shù)當作對象(object)使用,把閉包當作它的公用方法(Public Method),把內(nèi)部變量當作它的私有屬性(private value),這時一定要小心,不要隨便改變父函數(shù)內(nèi)部變量的值。

    解釋例子

    回到開始的例子,這是閉包的經(jīng)典的例子,這個和其他的例子和有些不一樣,我們分析一下,這里用了一個數(shù)組,其實這里我們執(zhí)行一次var fns = test(),形成了10個閉包,數(shù)組的每一個項存了一個閉包,這與其他的例子是不一樣的,其他的例子是函數(shù)執(zhí)行一次形成了一個閉包,所以這個10個閉包的初始的執(zhí)行環(huán)境是一樣的,每一個閉包使用了i這個變量,這個變量在函數(shù)var?fns =?test()執(zhí)行之后變?yōu)榱送顺鲅h(huán)的那個i的值10,JavaScript是解釋型的語言,所以在執(zhí)行數(shù)組中的閉包的時,會找到此時i的值10;看看arr的結(jié)果

    現(xiàn)在想怎樣解決這個問題呢?我們想想,這10個閉包形成時的執(zhí)行環(huán)境和活動對象是一樣的,現(xiàn)在考慮的就是要在初始時就不一樣,我們知道函數(shù)的作用域是一層一層的,那我們就需要在這之間家一層作用域,這層作用域要有不同的i的值,我們想到了自執(zhí)行匿名函數(shù),(funciton(){})(),我們把i的值穿進去,按值傳參就是相當于復(fù)制了一份變量嘛,在(funciton(){})()外部的作用域中的i的值的改變不會改變內(nèi)部的i的值,試一下

    function test(){var arr = [];for(var i = 0;i<10;i++){(function(i){ arr[i] = function(){return i;}})(i);}return arr; }var fns = test(); console.log(fns[9]()); // 值是9 console.log(fns[0]());//值是0

    當然也可以這樣

    function test(){var arr = [];for(var i = 0;i<10;i++){arr[i] = (function(i){return function(){return i}})(i);}return arr; }var fns = test(); console.log(fns[9]()); // 值是9 console.log(fns[0]());//值是0

    這兩個的實質(zhì)都是在閉包形成之前,給每一個閉包包上一層作用域,在這個作用域中傳一個參數(shù),是每一個閉包上一級的作用域中都有不同的i。當然還有其他的辦法這里不說了。

    小結(jié)

    閉包的應(yīng)用場景挺多的,在模塊化編程中很重要的,有些地方說函數(shù)也是閉包,還是那就話,概念不重要,理解會用才是最現(xiàn)實的。

    ?

    ?

    ?

    ?

    ?

    轉(zhuǎn)載于:https://www.cnblogs.com/allenxing/p/3578914.html

    總結(jié)

    以上是生活随笔為你收集整理的【javascript基础】8、闭包的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。