摘要:在不知道閉包之前,因?yàn)殒準(zhǔn)阶饔糜虻年P(guān)系只能向上一級(jí)的作用域查找變量,我一直認(rèn)為函數(shù)內(nèi)的變量在函數(shù)之外是無(wú)法訪問(wèn)到的。其實(shí)閉包的理解很簡(jiǎn)單,閉包就是能夠取得函數(shù)內(nèi)部變量的函數(shù)。在本質(zhì)上閉包其實(shí)就是鏈接函數(shù)內(nèi)部與函數(shù)外部的橋梁。
在不知道閉包之前,因?yàn)殒準(zhǔn)阶饔糜虻年P(guān)系(只能向上一級(jí)的作用域查找變量),我一直認(rèn)為函數(shù)內(nèi)的變量在函數(shù)之外是無(wú)法訪問(wèn)到的。直到認(rèn)識(shí)了你--閉包,讓我徹底顛覆了之前的想法,也讓我明白了不要隨便下結(jié)論,要不你會(huì)死的很慘……
談到閉包或許會(huì)讓你想到匿名函數(shù),因?yàn)檫@兩個(gè)神奇的小怪物會(huì)常常讓人混淆。閉包呢是指有權(quán)訪問(wèn)另一個(gè)函數(shù)作用域中的變量的函數(shù),我們創(chuàng)建閉包最常用的方法就是在一個(gè)函數(shù)內(nèi)部創(chuàng)建另一個(gè)函數(shù),以下面為例就是一個(gè)最簡(jiǎn)單的閉包:
function f(){ var num=10; function f1(){ return num; } return f1; } var result=f(); console.log(result()); //10
1、作用域
談?wù)撻]包之前呢還是需要想回顧一下變量的作用域問(wèn)題。
作用域無(wú)非就是全局變量和局部變量。
這樣有引出了作用域鏈,在作用域鏈只能向上一級(jí)查找變量,所以說(shuō)在函數(shù)內(nèi)部我們可以直接讀取全局變量,但在函數(shù)外部是無(wú)法讀取函數(shù)內(nèi)部的局部變量的。
我們都知道函數(shù)的執(zhí)行依賴(lài)于變量作用域,這個(gè)作用域是在函數(shù)定義時(shí)候決定的,而不是在函數(shù)調(diào)用時(shí)決定的。看下面例子:
var scope="global scope"; function checkscope(){ var scope="local scope"; function f(){ return scope; } return f; } checkscope()(); //local scope
嵌套的函數(shù)f()是定義在函數(shù)內(nèi)部的,其中的變量scope是局部變量,不管在何時(shí)何地執(zhí)行f(),作用域鏈?zhǔn)遣粫?huì)改變的。
var num1=10; function f1(){ var num2=20; num3=30; alert(num1); } f1(); //10 alert(num1); //10 alert(num2); //num2 is not defined alert(num3); //30
這里有一個(gè)需要注意的地方,函數(shù)內(nèi)部聲明變量一定要使用var,如果一不小心忘記了,那對(duì)不起了,你將聲明一個(gè)全局變量!
2、如何在函數(shù)外部讀取到函數(shù)內(nèi)部的變量呢?
但是在某種情況下因?yàn)橐恍┰蛭覀兪切枰玫胶瘮?shù)內(nèi)部的局部變量,但是前面我們也嘗試過(guò)了,在函數(shù)外部是無(wú)法得到內(nèi)部的變量的,怎么辦,這可愁死寶寶了……別急,辦法還是有的:
function f(){ var num=10; function f1(){ return num++; } return f1; } var count=f(); count(); //10 count(); //11 count(); //12 count(); //13 count(); //15
正如剛開(kāi)始我們的例子,f1被包含在了f內(nèi)部,此時(shí)f的變量對(duì)于f1都是所謂的上一級(jí),也就是都是可見(jiàn)的,隨時(shí)都可以訪問(wèn)。既然f1可以讀取f中的局部變量,那我們只需把f1作為返回值即可在f外部取得f的局部變量了。
3、到底什么是閉包?
其實(shí)上一段代碼中的f1函數(shù)就是我們說(shuō)的閉包。其實(shí)閉包的理解很簡(jiǎn)單,閉包就是能夠取得函數(shù)內(nèi)部變量的函數(shù)。
在本質(zhì)上閉包其實(shí)就是鏈接函數(shù)內(nèi)部與函數(shù)外部的橋梁。
4、閉包的用途
說(shuō)了好多,但是閉包到底有什么用,為什么要學(xué)習(xí)這個(gè)“違法”的怪物?閉包的用處實(shí)在是太多了,最最重要的就是:可以在函數(shù)外部讀取函數(shù)的變量,另一個(gè)就是讓這些變量的值永遠(yuǎn)保存在內(nèi)存中。what?莫急,請(qǐng)看下面代碼:
function f(){ var num1=99; add=function(){ num1+=1; } function f1(){ return num1; } return f1; } var result=f(); alert(result()); //99 add(); alert(result()); //100
為什么num1沒(méi)有在result()調(diào)用之后被清楚呢?原因就是f是f1的父函數(shù),f1最后是被賦值給了全局變量result,這導(dǎo)致了f1會(huì)一直在內(nèi)存當(dāng)中,然而f1的存在是依賴(lài)于f的,因此f也將一直在內(nèi)存中。
此處有玄機(jī)哦,注意add函數(shù)前面沒(méi)有var關(guān)鍵字,也就是說(shuō)add現(xiàn)在是一個(gè)全局變量,而add的值是一個(gè)匿名函數(shù)(另一個(gè)小怪物),而這個(gè)匿名函數(shù)本身也是一個(gè)閉包(因?yàn)樗L問(wèn)了局部變量num1),他可以在函數(shù)外部對(duì)函數(shù)內(nèi)部的變量進(jìn)行操作。
5、閉包里的this
我們都知道,this對(duì)象是在運(yùn)行時(shí)基于函數(shù)的執(zhí)行環(huán)境綁定的:全局作用域下,this就是window,而當(dāng)函數(shù)作為某個(gè)對(duì)象的方法調(diào)用時(shí),this等于那個(gè)對(duì)象(其實(shí)在全局作用于下,函數(shù)就是window的方法)。不過(guò)匿名函數(shù)的執(zhí)行環(huán)境是具有全局性的,因此this通常指向window。
var name="the window"; var object={ name:"my object", getNameFunc(){ return function(){ return this.name; } } } alert(object.getNameFunc()()); //"the window"
但不全是:
var name="the window"; var object={ name:"my object", getNameFunc:function(){ var that=this; return function(){ return that.name; }; } } alert(object.getNameFunc()()); //"my object"
6、使用閉包要小心
1)我們上面提到過(guò)閉包可以讓變量永久保存在內(nèi)存中,這會(huì)導(dǎo)致內(nèi)存消耗過(guò)大,所以使用閉包要謹(jǐn)慎,否則會(huì)帶來(lái)性能問(wèn)題,IE中會(huì)導(dǎo)致內(nèi)存泄漏。
function assigmHandler(){ var element=document.getElementById("myelement"); element.onclick=function(){ alert(element.id); } }
此后element將永遠(yuǎn)駐留在內(nèi)存中。解決辦法就是在退出函數(shù)之前把不是用的局部變量刪除。
function assigmHandler(){ var element=document.getElementById("myelement"); var id=element.id; element.onclick=function(){ alert(id); } element=null; }
2)閉包會(huì)在父函數(shù)外部改變父函數(shù)的變量,比如上面的例子:
function outer() { var obj = { name: "xiaoming" } return { number: obj, getObj: function () { console.log(obj) } } } var people = outer(); people.getObj(); //Object {name: "xiaoming"} people.number.name = "xiaozhang"; people.getObj(); //Object {name: "xiaozhang"}
如果你把父函數(shù)當(dāng)作對(duì)象使用,把閉包當(dāng)作它的公用方法,把內(nèi)部變量當(dāng)作它的私有屬性,這時(shí)一定要小心,不要隨便改變父函數(shù)內(nèi)部變量的值。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/86516.html
摘要:溫馨提示作者的爬坑記錄,對(duì)你等大神完全沒(méi)有價(jià)值,別在我這浪費(fèi)生命溫馨提示續(xù)本文將會(huì)成為一篇筆記類(lèi)型的文章,記錄閉包具體的應(yīng)用方式溫馨提示再續(xù)本文存在錯(cuò)誤,會(huì)慢慢改進(jìn)的,請(qǐng)不要把我說(shuō)的當(dāng)真在上一篇博文閉包不完全探索記錄閉包啥餡的中,對(duì)中 溫馨提示:作者的爬坑記錄,對(duì)你等大神完全沒(méi)有價(jià)值,別在我這浪費(fèi)生命溫馨提示-續(xù):本文(maybe)將會(huì)成為一篇筆記類(lèi)型的文章,記錄閉包具體的應(yīng)用方式溫馨...
摘要:閉包一詞來(lái)源于以下兩者的結(jié)合要執(zhí)行的代碼塊由于自由變量被包含在代碼塊中,這些自由變量以及它們引用的對(duì)象沒(méi)有被釋放和為自由變量提供綁定的計(jì)算環(huán)境作用域。在以及及以上等語(yǔ)言中都能找到對(duì)閉包不同程度的支持。 溫馨提示:作者的爬坑記錄,對(duì)你等大神完全沒(méi)有價(jià)值,別在我這浪費(fèi)生命 閉包,好吃嗎 ? 第一次聽(tīng)到這個(gè)詞,很不幸是在一次面試中,可想而知結(jié)果很細(xì)碎,從此閉包和跨域在我匱乏的前端知識(shí)中成為了...
溫馨提示:作者的爬坑記錄,對(duì)你等大神完全沒(méi)有價(jià)值,別在我這浪費(fèi)生命 這一切,源于阮大神博文學(xué)習(xí)Javascript閉包(Closure)- 阮一峰中的一道思考題 //問(wèn)題1: var name = The Window; var object = { name : My Object, getNameFunc : function(){ return function(){ ...
摘要:?jiǎn)卫J街饕菫榱私鉀Q對(duì)象的創(chuàng)建問(wèn)題。頁(yè)面中只放一個(gè)按鈕登錄實(shí)現(xiàn)得到登錄框元素綁定事件關(guān)閉彈框這里做登錄點(diǎn)擊頁(yè)面中的按鈕每次讓登錄框出現(xiàn)即可上面的代碼根據(jù)單例模式的使用構(gòu)造函數(shù)來(lái)實(shí)現(xiàn)的。 最近打算系統(tǒng)的學(xué)習(xí)javascript設(shè)計(jì)模式,以便自己在開(kāi)發(fā)中遇到問(wèn)題可以按照設(shè)計(jì)模式提供的思路進(jìn)行封裝,這樣可以提高開(kāi)發(fā)效率并且可以預(yù)先規(guī)避很多未知的問(wèn)題。 先從最基本的單例模式開(kāi)始。 什么是單例...
摘要:?jiǎn)卫J街饕菫榱私鉀Q對(duì)象的創(chuàng)建問(wèn)題。頁(yè)面中只放一個(gè)按鈕登錄實(shí)現(xiàn)得到登錄框元素綁定事件關(guān)閉彈框這里做登錄點(diǎn)擊頁(yè)面中的按鈕每次讓登錄框出現(xiàn)即可上面的代碼根據(jù)單例模式的使用構(gòu)造函數(shù)來(lái)實(shí)現(xiàn)的。 showImg(https://segmentfault.com/img/bVbiE4g?w=568&h=450);最近打算系統(tǒng)的學(xué)習(xí) Javascript 設(shè)計(jì)模式,以便自己在開(kāi)發(fā)中遇到問(wèn)題可以按照...
閱讀 3950·2021-11-16 11:50
閱讀 947·2021-11-11 16:55
閱讀 3672·2021-10-26 09:51
閱讀 872·2021-09-22 15:03
閱讀 3439·2019-08-30 15:54
閱讀 3272·2019-08-30 15:54
閱讀 2483·2019-08-30 14:04
閱讀 928·2019-08-30 13:53