這期內容當中的小編將會給大家?guī)碛嘘Pjavascript中閉包的使用技巧,以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
10年積累的成都網(wǎng)站設計、成都網(wǎng)站制作經(jīng)驗,可以快速應對客戶對網(wǎng)站的新想法和需求。提供各種問題對應的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡服務。我雖然不認識你,你也不認識我。但先做網(wǎng)站后付款的網(wǎng)站建設流程,更有赫山免費網(wǎng)站建設讓你可以放心的選擇與我們合作。
閉包是js開發(fā)慣用的技巧,什么是閉包?
閉包指的是:能夠訪問另一個函數(shù)作用域的變量的函數(shù)。清晰的講:閉包就是一個函數(shù),這個函數(shù)能夠訪問其他函數(shù)的作用域中的變量。eg:
function outer() { var a = '變量1' var inner = function () { console.info(a) } return inner // inner 就是一個閉包函數(shù),因為他能夠訪問到outer函數(shù)的作用域 }
很多人會搞不懂匿名函數(shù)與閉包的關系,實際上,閉包是站在作用域的角度上來定義的,因為inner訪問到outer作用域的變量,所以inner就是一個閉包函數(shù)。雖然定義很簡單,但是有很多坑點,比如this指向、變量的作用域,稍微不注意可能就造成內存泄露。我們先把問題拋一邊,思考一個問題:為什么閉包函數(shù)能夠訪問其他函數(shù)的作用域 ?
從堆棧的角度看待js函數(shù)
基本變量的值一般都是存在棧內存中,而對象類型的變量的值存儲在堆內存中,棧內存存儲對應空間地址。基本的數(shù)據(jù)類型: Number 、Boolean、Undefined、String、Null。
var a = 1 //a是一個基本類型 var b = {m: 20 } //b是一個對象
對應內存存儲:
當我們執(zhí)行 b={m:30}時,堆內存就有新的對象{m:30},棧內存的b指向新的空間地址( 指向{m:30} ),而堆內存中原來的{m:20}就會被程序引擎垃圾回收掉,節(jié)約內存空間。我們知道js函數(shù)也是對象,它也是在堆與棧內存中存儲的,我們來看一下轉化:
var a = 1; function fn(){ var b = 2; function fn1(){ console.log(b); } fn1(); } fn();
**
棧是一種先進后出的數(shù)據(jù)結構:
1 在執(zhí)行fn前,此時我們在全局執(zhí)行環(huán)境(瀏覽器就是window作用域),全局作用域里有個變量a;
2 進入fn,此時棧內存就會push一個fn的執(zhí)行環(huán)境,這個環(huán)境里有變量b和函數(shù)對象fn1,這里可以訪問自身執(zhí)行環(huán)境和全局執(zhí)行環(huán)境所定義的變量
3 進入fn1,此時棧內存就會push 一個fn1的執(zhí)行環(huán)境,這里面沒有定義其他變量,但是我們可以訪問到fn和全局執(zhí)行環(huán)境里面的變量,因為程序在訪問變量時,是向底層棧一個個找,如果找到全局執(zhí)行環(huán)境里都沒有對應變量,則程序拋出underfined的錯誤。
4 隨著fn1()執(zhí)行完畢,fn1的執(zhí)行環(huán)境被杯銷毀,接著執(zhí)行完fn(),fn的執(zhí)行環(huán)境也會被銷毀,只剩全局的執(zhí)行環(huán)境下,現(xiàn)在沒有b變量,和fn1函數(shù)對象了,只有a 和 fn(函數(shù)聲明作用域是window下)
**
在函數(shù)內訪問某個變量是根據(jù)函數(shù)作用域鏈來判斷變量是否存在的,而函數(shù)作用域鏈是程序根據(jù)函數(shù)所在的執(zhí)行環(huán)境棧來初始化的,所以上面的例子,我們在fn1里面打印變量b,根據(jù)fn1的作用域鏈的找到對應fn執(zhí)行環(huán)境下的變量b。所以當程序在調用某個函數(shù)時,做了一下的工作:準備執(zhí)行環(huán)境,初始函數(shù)作用域鏈和arguments參數(shù)對象
我們現(xiàn)在看回最初的例子outer與inner
function outer() { var a = '變量1' var inner = function () { console.info(a) } return inner // inner 就是一個閉包函數(shù),因為他能夠訪問到outer函數(shù)的作用域 } var inner = outer() // 獲得inner閉包函數(shù) inner() //"變量1"
當程序執(zhí)行完var inner = outer(),其實outer的執(zhí)行環(huán)境并沒有被銷毀,因為他里面的變量a仍然被被inner的函數(shù)作用域鏈所引用,當程序執(zhí)行完inner(), 這時候,inner和outer的執(zhí)行環(huán)境才會被銷毀調;《JavaScript高級編程》書中建議:由于閉包會攜帶包含它的函數(shù)的作用域,因為會比其他函數(shù)占用更多內容,過度使用閉包,會導致內存占用過多。
現(xiàn)在我們明白了閉包,已經(jīng)對應的作用域與作用域鏈,回歸主題:
坑點1: 引用的變量可能發(fā)生變化
function outer() { var result = []; for (var i = 0; i<10; i++){ result.[i] = function () { console.info(i) } } return result }
看樣子result每個閉包函數(shù)對打印對應數(shù)字,1,2,3,4,...,10, 實際不是,因為每個閉包函數(shù)訪問變量i是outer執(zhí)行環(huán)境下的變量i,隨著循環(huán)的結束,i已經(jīng)變成10了,所以執(zhí)行每個閉包函數(shù),結果打印10, 10, ..., 10
怎么解決這個問題呢?
function outer() { var result = []; for (var i = 0; i<10; i++){ result.[i] = function (num) { return function() { console.info(num); // 此時訪問的num,是上層函數(shù)執(zhí)行環(huán)境的num,數(shù)組有10個函數(shù)對象,每個對象的執(zhí)行環(huán)境下的number都不一樣 } }(i) } return result }
坑點2: this指向問題
var object = { name: ''object", getName: function() { return function() { console.info(this.name) } } } object.getName()() // underfined // 因為里面的閉包函數(shù)是在window作用域下執(zhí)行的,也就是說,this指向window
坑點3:內存泄露問題
function showId() { var el = document.getElementById("app") el.onclick = function(){ aler(el.id) // 這樣會導致閉包引用外層的el,當執(zhí)行完showId后,el無法釋放 } } // 改成下面 function showId() { var el = document.getElementById("app") var id = el.id el.onclick = function(){ aler(id) // 這樣會導致閉包引用外層的el,當執(zhí)行完showId后,el無法釋放 } el = null // 主動釋放el }
技巧1: 用閉包解決遞歸調用問題
function factorial(num) { if(num<= 1) { return 1; } else { return num * factorial(num-1) } } var anotherFactorial = factorial factorial = null anotherFactorial(4) // 報錯 ,因為最好是return num* arguments.callee(num-1),arguments.callee指向當前執(zhí)行函數(shù),但是在嚴格模式下不能使用該屬性也會報錯,所以借助閉包來實現(xiàn) // 使用閉包實現(xiàn)遞歸 function newFactorial = (function f(num){ if(num<1) {return 1} else { return num* f(num-1) } }) //這樣就沒有問題了,實際上起作用的是閉包函數(shù)f,而不是外面的函數(shù)newFactorial
** 技巧2:用閉包模仿塊級作用域**
es6沒出來之前,用var定義變量存在變量提升問題,eg:
for(var i=0; i<10; i++){ console.info(i) } alert(i) // 變量提升,彈出10 //為了避免i的提升可以這樣做 (function () { for(var i=0; i<10; i++){ console.info(i) } })() alert(i) // underfined 因為i隨著閉包函數(shù)的退出,執(zhí)行環(huán)境銷毀,變量回收
當然現(xiàn)在大多用es6的let 和const 定義。
上述就是小編為大家分享的javascript中閉包的使用技巧了,如果您也有類似的疑惑,不妨礙參照上述分析進行理解。如果想了解更多相關內容,請關注創(chuàng)新互聯(lián)行業(yè)資訊。
網(wǎng)頁題目:javascript中閉包的使用技巧
網(wǎng)站地址:http://jinyejixie.com/article34/ijccse.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供用戶體驗、做網(wǎng)站、虛擬主機、、全網(wǎng)營銷推廣、響應式網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經(jīng)允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)