新手入門js閉包學(xué)習(xí)過程解析
閉包,其實是一種語言特性,它是指的是程序設(shè)計語言中,允許將函數(shù)看作對象,然后能像在對象中的操作般在函數(shù)中定義實例(局部)變量,而這些變量能在函數(shù)中保存到函數(shù)的實例對象銷毀為止,其它代碼塊能通過某種方式獲取這些實例(局部)變量的值并進行應(yīng)用擴展。
我們的理解:
其實閉包就是一個函數(shù),一個外部函數(shù)通過調(diào)用函數(shù)并return返回出內(nèi)部函數(shù),這里的內(nèi)部函數(shù)就是一個閉包;此時在內(nèi)部函數(shù)中是可以訪問到外部函數(shù)的變量的;
要想理解閉包,首先我們要了解棧堆內(nèi)存和作用域鏈;首先我們來講解棧堆內(nèi)存:
首先我們來看個demo:
var a=1; var obj={"name":"咸魚"}
上面簡單的兩句代碼,其實就是在內(nèi)存中做了兩件事,效果圖如下:
在js簡單實現(xiàn)深淺拷貝(http://www.dbjr.com.cn/article/171389.htm)一文中我們知道基本數(shù)據(jù)類型是存儲在棧內(nèi)存中的,引用數(shù)據(jù)類型是存儲在堆內(nèi)存中的,其實上面的兩句代碼在內(nèi)存中就是做了兩件事:1.首先在棧內(nèi)存中開辟了一塊空間用來存放a的變量和值;2.在堆內(nèi)存中開辟了一塊空間用來存儲obj的值,同時在將地址指向棧內(nèi)存中的變量名obj
如果我們在代碼下面再加上一句obj={"name":'張三"},這個時候我們之前存儲name為咸魚的值也就是obj原來的值會被js中的垃圾回收機制回收掉,然后obj的值重新的指向{name:"張三"}這個值;
作用域鏈
再來看一下這個例子:
var a = 1; function fn(){ var b = 2; function fn1(){ console.log(b);//2 console.log(a);//1 } fn1(); } fn();
效果圖如下:
1.var a=1;這個時候我們是在全局執(zhí)行環(huán)境的,瀏覽器的全局環(huán)境就是window作用域,我們的window作用域中有a和fn;
2.當(dāng)我們往下走到fn的時候,棧內(nèi)存會開辟一塊新的執(zhí)行環(huán)境,此時fn的執(zhí)行環(huán)境中我們有b和fn1;
3.當(dāng)我們接著往下走到fn1的時候,這時棧內(nèi)存同樣會開辟一塊新的執(zhí)行環(huán)境,此時fn1的執(zhí)行環(huán)境中是沒有任何變量數(shù)據(jù)的,但是我們在fn1中輸出a、b,我們都是可以讀取到的;這是因為程序在讀取變量的時候是從內(nèi)到外的開始讀的,是隨著fn1開始往上一層一層的查找,是這樣的執(zhí)行順序(fn1 = > fn = > window),如果找到window中還沒有讀取到變量,這時程序才會報錯;
當(dāng)然在執(zhí)行的過程中,垃圾回收機制如果檢測到程序執(zhí)行完了是會進行垃圾回收的,避免造成內(nèi)存泄露等問題;就是說我們的fn1里面執(zhí)行完之后fn1的作用域就會被銷毀,接著程序執(zhí)行fn,fn執(zhí)行完之后fn就會被銷毀;往上執(zhí)行到全局的時候,整個程序就沒有了fn的作用域和fn1的作用域,只剩下瀏覽器的全局作用域window,這個時候window里只剩a和fn;
了解了上面的作用域鏈和棧內(nèi)存和堆內(nèi)存的知識之后,我們來開始講解js閉包:
function outer() { var a = '123' return function add(){ //在這里因為作用域的關(guān)系,add是能訪問到outer的所有變量的,但是outer是訪問不到add的變量; //所以思路一轉(zhuǎn),把add的值作為結(jié)果return出來變通實現(xiàn)outer外部函數(shù)訪問到了內(nèi)部函數(shù)變量 // add就是一個閉包函數(shù),因為他能夠訪問到outer函數(shù)的作用域,add中沒有找到變量a,則會繼續(xù)往上層作用域找 console.log(a); } } var inner = outer() // 獲得add閉包函數(shù) inner() //"123"
首先我們可以看到,在全局作用域下我們是有一個outer函數(shù)的,outer作用域里面有a和add,add作用域里面執(zhí)行控制臺輸出a的變量,此時這里的add函數(shù)就形成了一個閉包,因為add函數(shù)里面需要訪問到outer作用域下的a變量,而他們不處在同一個作用域中,所以兩者相互牽引,需要輸出a,上面outer中的變量a就必須得在,作用域鏈查找到outer的時候找到a了,輸出a的時候,垃圾回收機制會認(rèn)為add還沒有執(zhí)行完成,因為此時的作用域鏈查找已經(jīng)到了outer作用域下,所以不會清理a的內(nèi)存空間;所以這就會帶來一個問題:如果我們多次的使用閉包,則會給我們的程序帶來內(nèi)存占用過多,導(dǎo)致性能問題;
函數(shù)內(nèi)部能訪問全局變量是javascript語言的特殊之處,但是如果我們想達到函數(shù)外部能訪問內(nèi)部變量的時候,我們就可以使用閉包,這就是閉包給我們帶來的便利;
閉包的優(yōu)缺點:
優(yōu)點:
1.可以讀取函數(shù)內(nèi)部的變量
2.可以避免全局污染
缺點:
1.閉包會導(dǎo)致變量不會被垃圾回收機制所清除,會大量消耗內(nèi)存;
2.不恰當(dāng)?shù)氖褂瞄]包可能會造成內(nèi)存泄漏的問題;
總結(jié):
1.作用域鏈查找變量的方式是一層一層的往上查找,直到找到為止,如果找到window全局作用域還未找到,就報undefined;
2.嵌套函數(shù)中,因為不在同一作用域,正常情況下內(nèi)外部函數(shù)是訪問不到內(nèi)部函數(shù)的,但是通過閉包可以實現(xiàn);
3.盡可能少的使用閉包,因為會造成內(nèi)存消耗大以及有可能造成內(nèi)存泄露(如果不需要的時候,不要隨便使用);
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- JS閉包原理及其使用場景解析
- JavaScript 閉包的使用場景
- 深入了解JS之作用域和閉包
- JavaScript閉包原理與用法學(xué)習(xí)筆記
- JavaScript 函數(shù)用法詳解【函數(shù)定義、參數(shù)、綁定、作用域、閉包等】
- js 閉包深入理解與實例分析
- JavaScript閉包相關(guān)知識解析
- JS頁面獲取 session 值,作用域和閉包學(xué)習(xí)筆記
- JS常見面試試題總結(jié)【去重、遍歷、閉包、繼承等】
- JS實現(xiàn)利用閉包判斷Dom元素和滾動條的方向示例
- JS學(xué)習(xí)筆記之閉包小案例分析
- js核心基礎(chǔ)之閉包的應(yīng)用實例分析
- js閉包的9個使用場景
相關(guān)文章
解決typescript項目報錯:找不到模塊“xxx”或其相應(yīng)的類型聲明問題
這篇文章主要介紹了解決typescript項目報錯:找不到模塊“xxx”或其相應(yīng)的類型聲明問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06javascript經(jīng)典特效分享 手風(fēng)琴、輪播圖、圖片滑動
這篇文章主要介紹了javascript經(jīng)典特效,手風(fēng)琴、輪播圖、圖片滑動效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-09-09avascript中的自執(zhí)行匿名函數(shù)應(yīng)用示例
javascript中的自執(zhí)行匿名函數(shù)可以用它創(chuàng)建命名空間,只要把自己所有的代碼都寫在這個特殊的函數(shù)包裝內(nèi),那么外部就不能訪問,除非你允許2014-09-09一款JavaScript壓縮工具:X2JSCompactor
一款JavaScript壓縮工具:X2JSCompactor...2007-06-06AJAX使用了UpdatePanel后無法使用alert彈出腳本
今天在做項目的時候發(fā)現(xiàn)個問題。。使用UpdatePanel后alert腳本就要報錯了。2010-04-04關(guān)于foreach循環(huán)中遇到的問題小結(jié)
這篇文章主要介紹了關(guān)于foreach循環(huán)中遇到的問題總結(jié),非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-05-05