亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么徹底搞懂JS閉包各種坑

發布時間:2021-12-14 11:37:54 來源:億速云 閱讀:158 作者:柒染 欄目:開發技術

本篇文章為大家展示了怎么徹底搞懂JS閉包各種坑,內容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。

老司機帶你徹底搞懂JS閉包各種坑

閉包是js開發慣用的技巧,什么是閉包?

閉包指的是:能夠訪問另一個函數作用域的變量的函數。清晰的講:閉包就是一個函數,這個函數能夠訪問其他函數的作用域中的變量。eg:

function outer() {     
var  a = '變量1'     
var  inner = function () {            
console.info(a)     }   
 return inner    
 // inner 就是一個閉包函數,因為他能夠訪問到outer函數的作用域}

很多人會搞不懂匿名函數與閉包的關系,實際上,閉包是站在作用域的角度上來定義的,因為inner訪問到outer作用域的變量,所以inner就是一個閉包函數。雖然定義很簡單,但是有很多坑點,比如this指向、變量的作用域,稍微不注意可能就造成內存泄露。我們先把問題拋一邊,思考一個問題:為什么閉包函數能夠訪問其他函數的作用域 ?

從堆棧的角度看待js函數

基本變量的值一般都是存在棧內存中,而對象類型的變量的值存儲在堆內存中,棧內存存儲對應空間地址。基本的數據類型: Number 、Boolean、Undefined、String、Null。

var  a = 1   //a是一個基本類型var  b = {m: 20 }   //b是一個對象

對應內存存儲:

當我們執行 b={m:30}時,堆內存就有新的對象{m:30},棧內存的b指向新的空間地址( 指向{m:30} ),而堆內存中原來的{m:20}就會被程序引擎垃圾回收掉,節約內存空間。我們知道js函數也是對象,它也是在堆與棧內存中存儲的,我們來看一下轉化:

var a = 1;function fn(){    
var b = 2;    
function fn1(){        
console.log(b);
    }    
   fn1();
  }fn();

**

棧是一種先進后出的數據結構:

1 在執行fn前,此時我們在全局執行環境(瀏覽器就是window作用域),全局作用域里有個變量a;

2 進入fn,此時棧內存就會push一個fn的執行環境,這個環境里有變量b和函數對象fn1,這里可以訪問自身執行環境和全局執行環境所定義的變量

3 進入fn1,此時棧內存就會push 一個fn1的執行環境,這里面沒有定義其他變量,但是我們可以訪問到fn和全局執行環境里面的變量,因為程序在訪問變量時,是向底層棧一個個找,如果找到全局執行環境里都沒有對應變量,則程序拋出underfined的錯誤。

4 隨著fn1()執行完畢,fn1的執行環境被杯銷毀,接著執行完fn(),fn的執行環境也會被銷毀,只剩全局的執行環境下,現在沒有b變量,和fn1函數對象了,只有a 和 fn(函數聲明作用域是window下)


在函數內訪問某個變量是根據函數作用域鏈來判斷變量是否存在的,而函數作用域鏈是程序根據函數所在的執行環境棧來初始化的,所以上面的例子,我們在fn1里面打印變量b,根據fn1的作用域鏈的找到對應fn執行環境下的變量b。所以當程序在調用某個函數時,做了一下的工作:準備執行環境,初始函數作用域鏈和arguments參數對象

我們現在看回最初的例子outer與inner

function outer() {     var  a = '變量1'     var  inner = function () {            console.info(a)     }    return inner    // inner 就是一個閉包函數,因為他能夠訪問到outer函數的作用域}var  inner = outer()   // 獲得inner閉包函數inner()   //"變量1"

當程序執行完var inner = outer(),其實outer的執行環境并沒有被銷毀,因為他里面的變量a仍然被被inner的函數作用域鏈所引用,當程序執行完inner(), 這時候,inner和outer的執行環境才會被銷毀調;《JavaScript高級編程》書中建議:由于閉包會攜帶包含它的函數的作用域,因為會比其他函數占用更多內容,過度使用閉包,會導致內存占用過多。

現在我們明白了閉包,已經對應的作用域與作用域鏈,回歸主題:

坑點1: 引用的變量可能發生變化

function outer() {      
var result = [];      
for (var i = 0; i<10; i++){        
result.[i] = function () {            
console.info(i)       
 }    
  }     
  return result}

看樣子result每個閉包函數對打印對應數字,1,2,3,4,...,10, 實際不是,因為每個閉包函數訪問變量i是outer執行環境下的變量i,隨著循環的結束,i已經變成10了,所以執行每個閉包函數,結果打印10, 10, ..., 10

怎么解決這個問題呢?

function outer() {      
var result = [];      
for (var i = 0; i<10; i++){        
result.[i] = function (num) {             
return function() {                   
console.info(num);    
// 此時訪問的num,是上層函數執行環境的num,數組有10個函數對象,每個對象的執行環境下的number都不一樣             }        }(i)     }     return result}

坑點2: this指向問題

var object = {     
name: ''object",     
getName: function() {        
return function() {             
console.info(this.name) 
       }    
  }}object.getName()()   
   // underfined// 
   因為里面的閉包函數是在window作用域下執行的,也就是說,this指向window

坑點3:內存泄露問題

function  showId() {    
var el = document.getElementById("app")    
el.onclick = function(){      
aler(el.id)   
// 這樣會導致閉包引用外層的el,當執行完showId后,el無法釋放   
 }}// 改成下面function  showId() {    var el = document.getElementById("app")    
 var id  = el.id    el.onclick = function(){      
 aler(id)   // 這樣會導致閉包引用外層的el,當執行完showId后,el無法釋放 
    }   
     el = null    // 主動釋放el}

技巧1: 用閉包解決遞歸調用問題

function  factorial(num) {   
if(num<= 1) {       return 1;   } else {      
return num * factorial(num-1)  
 }}var anotherFactorial = factorialfactorial = nullanotherFactorial(4)   
 // 報錯 ,因為最好是return num* arguments.callee(num-1),arguments.callee指向當前執行函數,但是在嚴格模式下不能使用該屬性也會報錯,所以借助閉包來實現
 // 使用閉包實現遞歸function newFactorial = (function f(num){   
  if(num<1) {return 1}    
  else {       
  return num* f(num-1)  
    }}) 
 //這樣就沒有問題了,實際上起作用的是閉包函數f,而不是外面的函數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隨著閉包函數的退出,執行環境銷毀,變量回收

上述內容就是怎么徹底搞懂JS閉包各種坑,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

js
AI

凤台县| 扎囊县| 中江县| 奈曼旗| 北辰区| 平阳县| 法库县| 三台县| 隆回县| 永福县| 澄迈县| 安西县| 竹山县| 侯马市| 阿坝| 梅州市| 如东县| 泽库县| 响水县| 阳朔县| 巴里| 沾化县| 恩平市| 吴桥县| 呼伦贝尔市| 咸宁市| 仪陇县| 吴旗县| 岑溪市| 金溪县| 商城县| 高密市| 柳江县| 鸡西市| 安吉县| 桐庐县| 长子县| 千阳县| 松溪县| 阜新市| 文山县|