js閉包原理及使用
阿新 • • 發佈:2021-02-15
技術標籤:jsjsvuejavascripthtml5html
保持對程式碼的熱愛並保持懷疑態度
昨天寫了一篇點選檢視js防抖和節流的理解用到了閉包,剛剛好我也忘的差不多了,只知道閉包長什麼,原理什麼的早就忘到九霄雲外了,所以準備重新梳理了一下閉包。
閉包的概念及原理
閉包是指有權訪問另外一個函式作用域中的變數的函式。
也可以這麼說,這樣可能更能理解,也就是閉包的原理:
通過作用域的巢狀,觸發計算機的垃圾回收機制(硬碟),將原本的區域性變數進化成私有變數的環境,叫閉包
計算機的垃圾回收機制
-
硬碟:需要刪除的資料,不直接刪除,而是再是儲存在一個臨時區域,如果需要繼續使用,可以從臨時區域取出
-
記憶體:需要刪除的資料,直接刪除,不可恢復
-
程式,或程式碼的執行,預設使用的是記憶體的垃圾回收,閉包使用的是硬碟的垃圾回收
例項
首先來看這麼一個需求, 要求多次執行a的值累加,不允許出現全域性變數
function fn() {
var a = 10;
a++
console.log(a);
}
fn()
fn()
如果這麼寫的話,a只會輸出11,因為函式每次都是重新執行。
但是這麼寫呢
function fn() {
var a = 10;
return function (){
a++;
console.log(a);
}
}
var f=fn();
f();
f();
f();
很顯然是可以實現累加的,沒錯這就是閉包
例項分析
在分析上訴程式碼中,首先得知道這麼幾個知識
關於函式的定義作用域和執行作用域
- 定義函式時的作用域,叫定義作用域
- 執行函式時的作用域,叫執行作用域
- 無論函式在哪執行,都可以在自身內部使用自身定義作用域中的資料
拿上面的例子來分析
function fn() { 定義 var a = 10; 這個a相對於下面一個函式相當於是父級 return function(){ 定義返回值 a++; 每次都累加a,這裡也沒有宣告a,所以會直接會改變a的值,執行一次就改變一次,所以就實現了累加。 console.log(a); } } var f=fn(); 賦值 f(); 執行 ,函式在執行時,可以拿到自身的定義作用域的變數 f(); f();
也可以這麼寫,通過傳參的方式
function fn(a) {
此時的a是fn的形參。通過作用域的巢狀,a此時相當於返回函式的父級作用域 ,也可以實現累加
return function(){
a++;
console.log(a);
}
}
var f=fn(10);
f();
f();
f();
閉包的特點
- 可以在作用域外部修改作用域內部的資料
- 連線了作用域內外部的橋樑
- 消耗記憶體
- 記憶體洩漏(記憶體溢位)(低版本瀏覽器以下)
閉包的應用
可以給內建的方法的回撥函式傳參
看例項
setTimeout(fn("hello"), 1000);
function fn(a){
console.log(a);
}
如果直接這麼寫的話,不會等到1秒鐘以後再console.log,而是立即執行,為什麼呢,涉及到了巨集任務和微任務,點選檢視非同步,同步,巨集任務,微任務理解但這不是重點,重點我們用閉包來解決這個問題
setTimeout(fn("hello"), 1000);
function fn(a){
return function(){
console.log(a);
}
}
這裡就是通過傳參的方式來解決的。
迴圈中的事件內使用迴圈的變數
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
</style>
</head>
<body>
<ul>
<li>link1</li>
<li>link2</li>
<li>link3</li>
<li>link4</li>
<li>link5</li>
</ul>
</body>
<script>
// 迴圈中的事件內使用迴圈的變數
var ali = document.querySelectorAll("li")
for(var i=0;i<ali.length;i++){
(function(i){
ali[i].onclick = function(){
console.log(i);
}
})(i);
}
for(var i=0;i<ali.length;i++){
ali[i].onclick = (function(i){
return function(){
console.log(i);
}
})(i);
}
for(let i=0;i<ali.length;i++){
ali[i].onclick = function(){
console.log(i);
}
}
</script>
</html>
以上的三種方式均可以獲取到li的索引。