1.有關閉包定義javascript
閉包是指有權訪問另外一個函數做用域中變量的函數,建立閉包的最多見的 方式就是在一個函數內建立另外一個函數,經過另外一個函數訪問這個函數的局部變量 閉包的特性: 函數內再嵌套函數 內部函數能夠引用外層的參數和變量 參數和變量不會被垃圾回收機制回收
說說你對閉包的理解html
使用閉包主要是爲了設計私有的方法和變量。閉包的優勢是能夠避免全局變量的污染, 缺點是閉包會常駐內存,會增大內存使用量,使用不當很容易形成內存泄露。在js中, 函數即閉包,只有函數纔會產生做用域的概念 閉包 的最大用處有兩個,一個是能夠讀取函數內部的變量,另外一個就是讓這些 變量始終保持在內存中 閉包的另外一個用處,是封裝對象的私有屬性和私有方法 好處:可以實現封裝和緩存等; 壞處:就是消耗內存、不正當使用會形成內存溢出的問題
使用閉包的注意點java
因爲閉包會使得函數中的變量都被保存在內存中,內存消耗很大,因此不能濫用 閉包,不然會形成網頁的性能問題,在IE中可能致使內存泄露 解決方法是,在退出函數以前,將不使用的局部變量所有刪除 閉包的定義其實很簡單:函數 A 內部有一個函數 B,函數 B 能夠訪問到函數 A 中的變量,那麼函數 B 就是閉包 function A() { let a = 1 window.B = function () { console.log(a) } } A() B() // 1
閉包會產生一個很經典的問題:面試
多個子函數的[[scope]]都是同時指向父級,是徹底共享的。所以當父級的 變量對象被修改時,全部子函數都受到影響。
解決:數組
變量能夠經過 函數參數的形式 傳入,避免使用默認的[[scope]]向上查找 使用setTimeout包裹,經過第三個參數傳入 使用 塊級做用域,讓變量成爲本身上下文的屬性,避免共享
2.閉包簡單例子
指的是有權訪問另外一個函數做用域中變量的函數,
建立閉包的常見方式,就是在一個函數內部建立另外一個函數。瀏覽器
function f1(){ var n=999; function f2(){ alert(n); // 999 } }
function f1(){ var n=999; function f2(){ alert(n); } return f2; } var result=f1(); result(); // 999
3.閉包的用處:緩存
閉包能夠用在許多地方。它的最大用處有兩個,一個是前面提到的能夠讀取函數內部的變量,另外一個就是讓這些變量的值始終保持在內存中。閉包
function f1(){ var n=999; nAdd=function(){n+=1} function f2(){ alert(n); } return f2; } var result=f1(); result(); // 999 nAdd(); result(); // 1000
4.使用必閉包的問題:異步
因爲閉包會使得函數中的變量都被保存在內存中,內存消耗很大,因此不能濫用閉包,不然會形成網頁的性能問題。函數
閉包的例子:
function outerFun() { var a=0; function innerFun() { a++; alert(a); } return innerFun; //注意這裏 } var obj=outerFun(); obj(); //結果爲1 obj(); //結果爲2 var obj2=outerFun(); obj2(); //結果爲1 obj2(); //結果爲2
function outerFun() { //沒有var a =0; alert(a); } var a=4; outerFun(); alert(a); 結果爲 0,0 真是奇怪,爲何呢? 做用域鏈是描述一種路徑的術語,沿着該路徑能夠肯定變量的值 .當執行a=0時,因 爲沒有使用var關鍵字,所以賦值操做會沿着做用域鏈到var a=4; 並改變其值.
5.閉包內的微觀世界
參考學習:https://www.cnblogs.com/goloving/p/7062212.html
若是要更加深刻的瞭解閉包以及函數a和嵌套函數b的關係,咱們須要引入另外幾個概念:函數的執行環境(excution context)、活動對象(call object)、做用域(scope)、做用域鏈(scope chain)。以函數a從定義到執行的過程爲例闡述這幾個概念。
1.當定義函數a的時候,js解釋器會將函數a的做用域鏈(scope chain)設置爲定義a時a所在的「環境」,若是a是一個全局函數,則scope chain中只有window對象。
當執行函數a的時候,a會進入相應的執行環境(excution context)。
2.在建立執行環境的過程當中,首先會爲a添加一個scope屬性,即a的做用域,其值就爲第1步中的scope chain。即a.scope=a的做用域鏈。
3.而後執行環境會建立一個活動對象(call object)。活動對象也是一個擁有屬性的對象,但它不具備原型並且不能經過Javascript代碼直接訪問。建立完活動對象後,把活動對象添加到a的做用域鏈的最頂端。此時a的做用域鏈包含了兩個對象:a的活動對象和window對象。
4.下一步是在活動對象上添加一個arguments屬性,它保存着調用函數a時所傳遞的參數。
5.最後把全部函數a的形參和內部的函數b的引用也添加到a的活動對象上。在這一步中,完成了函數b的的定義,所以如同第3步,函數b的做用域鏈被設置爲b所被定義的環境,即a的做用域。
當在函數b中訪問一個變量的時候,搜索順序是:
先搜索自身的活動對象,若是存在則返回,若是不存在將繼續搜索函數a的活動對象, 依次查找,直到找到爲止。 若是函數b存在prototype原型對象,則在查找完自身的活動對象後先查找自身的原型 對象,再繼續查找。這就是Javascript中的變量查找機制。 若是整個做用域鏈上都沒法找到,則返回undefined。
函數的定義與執行。文中提到函數的做用域是在定義函數時候就已經肯定,而不是在執行的時候肯定
6.有關閉包經典案例
經典面試題,循環中使用閉包解決 var 定義函數的問題
for ( var i=1; i<=5; i++) { setTimeout( function timer() { console.log( i ); }, i*1000 ); }
首先由於 setTimeout 是個異步函數,全部會先把循環所有執行完畢,這時候 i 就是 6 了,因此會輸出一堆 6。
解決辦法兩種,第一種使用閉包
for (var i = 1; i <= 5; i++) { (function(j) { setTimeout(function timer() { console.log(j); }, j * 1000); })(i); }
第二種就是使用 setTimeout 的第三個參數
for ( var i=1; i<=5; i++) { setTimeout( function timer(j) { console.log( j ); }, i*1000, i); }
第三種就是使用 let 定義 i 了
for ( let i=1; i<=5; i++) { setTimeout( function timer() { console.log( i ); }, i*1000 ); }
有關內存溢出與內存泄漏
1. 內存溢出 * 一種程序運行出現的錯誤 * 當程序運行須要的內存超過了剩餘的內存時, 就出拋出內存溢出的錯誤 2. 內存泄露 * 佔用的內存沒有及時釋放 * 內存泄露積累多了就容易致使內存溢出 * 常見的內存泄露: * 意外的全局變量 * 沒有及時清理的計時器或回調函數 * 閉包 // 1. 內存溢出 var obj = {} for (var i = 0; i < 10000; i++) { obj[i] = new Array(10000000) console.log('-----') } // 2. 內存泄露 // 意外的全局變量 function fn() { a = new Array(10000000) console.log(a) } fn() // 沒有及時清理的計時器或回調函數 var intervalId = setInterval(function () { //啓動循環定時器後不清理 console.log('----') }, 1000) // clearInterval(intervalId) // 閉包 function fn1() { var a = 4 function fn2() { console.log(++a) } return fn2 } var f = fn1() f() // f = null
7.js垃圾回收機制
轉載:https://www.cnblogs.com/zhwl/p/4664604.html
因爲字符串、對象和數組沒有固定大小,當他們的大小已知時,才能對他們進行動態的存儲分配。JavaScript程序每次建立字符串、數組或對象時,解釋器都必須分配內存來存儲那個實體。只要像這樣動態地分配了內存,最終都要釋放這些內存以便他們可以被再用,不然,JavaScript的解釋器將會消耗完系統中全部可用的內存,形成系統崩潰。
如今各大瀏覽器一般用採用的垃圾回收有兩種方法:標記清除、引用計數。
標記清除
這是javascript中最經常使用的垃圾回收方式。當變量進入執行環境是,就標記這個變量爲「進入環境」。從邏輯上講,永遠不能釋放進入環境的變量所佔用的內存,由於只要執行流進入相應的環境,就可能會用到他們。當變量離開環境時,則將其標記爲「離開環境」。
引用計數 另外一種不太常見的垃圾回收策略是引用計數。引用計數的含義是跟蹤記錄每一個值被引用的次數。當聲明瞭一個變量並將一個引用類型賦值給該變量時,則這個值的引用次數就是1。相反,若是包含對這個值引用的變量又取得了另一個值,則這個值的引用次數就減1。當這個引用次數變成0時,則說明沒有辦法再訪問這個值了,於是就能夠將其所佔的內存空間給收回來。這樣,垃圾收集器下次再運行時,它就會釋放那些引用次數爲0的值所佔的內存。