標籤:length 例子 com 對象 機制 ams cti for迴圈 數字
摘要
閉包機制是JavaScript的重點和痛點,本文希望能協助大家輕鬆的學習閉包
一、什麼是閉包?
閉包就是可以訪問另一個函數範圍中變數的函數。
下面列舉出常見的閉包實現方式,以例子講解閉包概念
function f1(){ var n=999; nAdd=function(){n+=1} function f2(){ alert(n); } return f2; } var result=f1(); result(); // 999 nAdd(); result(); // 1000
f1是f2的父函數,而f2被賦給了一個全域變數(return的值),這導致f2始終在記憶體中,而f2的存在依賴於f1,因此f1也始終在記憶體中,不會在調用結束後,被記憶體回收機制(garbage collection)回收,這便形成了閉包。
因此,可以這麼理解。閉包機制就是,如果A函數引用了另一個函數B的變數,但是B返回後A仍沒有返回,仍存在,因為A的引用,所以B的所有局部變數並不會隨B退出而登出,會一直存在,直到A登出。此時A就是閉包。
二、閉包的this指標
閉包通常在全域環境調用的,所以this通常指向window,具體情況還是需要視執行環境而言,總之this指向執行環境。
若需要閉包的this指向閉包的包含對象,則需要將包含對象的this作為變數傳進閉包。
三、使用閉包的注意點
由於閉包會使得函數中的變數都被儲存在記憶體中,記憶體消耗很大,所以不能濫用閉包,否則會造成網頁的效能問題,在IE中可能導致記憶體泄露。解決方案是,在退出函數之前,將不使用的局部變數全部刪除。
閉包會在父函數外部,改變父函數內部變數的值。所以,如果你把父函數當作對象(object)使用,把閉包當作它的公用方法(Public Method),把內部變數當作它的私人屬性(private value),這時一定要小心,不要隨便改變父函數內部變數的值。
四、解決一道閉包常見面試題
問題:
function onMyLoad(){ /* 拋出問題: 此題的目的是想每次點擊對應目標時彈出對應的數字下標 0~4,但實際是無論點擊哪個目標都會彈出數字5 問題所在: arr 中的每一項的 onclick 均為一個函數執行個體(Function 對象),這個函數執行個體也產生了一個閉包域, 這個閉包域引用了外部閉包域的變數,其 function scope 的 closure 對象有個名為 i 的引用, 外部閉包域的私人變數內容發生變化,內部閉包域得到的值自然會發生改變 */ var arr = document.getElementsByTagName("p"); for(var i = 0; i < arr.length;i++){ arr[i].onclick = function(){ alert(i); } } }
解決方案
1、在外面再加一層函數,將i作為函數參數傳進來,這樣每次儲存的是函數內部的變數,與外部i不是同一個記憶體空間,而每次調用函數都會產生一個局部變數,所以可以保證每次儲存的值互不影響。
for(var i = 0; i<arr.length;i++){ arr[i].onclick = (function(arg){ return function () { alert(arg); } })(i);}
2、用ES6新特let,將for迴圈的var i改成let i,這樣當前的i只在本輪迴圈有效,所以每一次迴圈的i其實都是一個新的變數。你可能會問,如果每一輪迴圈的變數i都是重新聲明的,那它怎麼知道上一輪迴圈的值,從而計算出本輪迴圈的值?這是因為 JavaScript 引擎內部會記住上一輪迴圈的值,初始化本輪的變數i時,就在上一輪迴圈的基礎上進行計算。
輕鬆理解JavaScript閉包