有關做用域鏈和原型鏈對於學習js的小夥伴都應該不陌生,今天我們就看看最詳細的做用域鏈解釋,目錄中可能有些一些不常見的名詞,可是這些對於理解做用域鏈和閉包頗有做用,並且基本均可以理解js運行機制了😁前端
代碼實現:web
const result = add(3,2)// 使用function直接聲明非匿名函數能夠實現「函數聲明提高」 function add(a,b){ return a+b } const value = add(1,2) 複製代碼
圖片展現:數組
產物:瀏覽器
執行中須要讀取的變量都會從該做用域鏈的前端查找(圖中的Local對象),查找過程當中若是沒有找到須要的變量,就會一直找到做用鏈的最頂端window(圖中的Global對象),這就是做用域鏈的查找 bash
注意:
全局執行環境一直會存在,當關閉瀏覽器時纔會被銷燬markdown
1 當執行流到當前函數時,該函數的執行環境就會被推入到一個環境棧(能夠理解爲包含該函數的函數執行環境)中執行閉包
2 當函數執行環境完畢後(無返回引用類型),該執行環境就會被彈出,把執行權還給以前的執行環境。函數
3 此時函數的執行環境會被銷燬,裏面的變量對象和做用域鏈都會被銷燬學習
語言實現:
當一個函數中返回一個引用地址並賦值給一個變量時,就會成一個閉包,只有當變量的引用地址改變或者爲null時,js的垃圾回收機制不定時觸發是,閉包纔會被銷燬
優化
代碼實現:
function process (a){ return function(b){ return a+b } } const add =process(3) add(2)// 5 add=null 複製代碼
缺點:
因爲閉包會攜帶包含它的函數的做用域,所以會比其餘函數佔用更多的內存。過 度使用閉包可能會致使內存佔用過多,咱們建議讀者只在絕對必要時再考慮使用閉 包。雖然像 V8 等優化後的 JavaScript 引擎會嘗試回收被閉包占用的內存,但請你們 仍是要慎重使用閉包
function createFn(){ var result =[]; for(var i=0;i<10;i++){ result[i]=function(){ console.log(i)} } return result; } var res=createFn() res[0]() 複製代碼
這個問題你們應該都碰見過,打印的是0仍是10呢,正確答案是10 爲何是10呢,我們看看"res【0】()"的做用鏈裏面包含了幾個 變量對象 答案是3個
我們看看,上面的代碼的執行順序
當createFn()執行時
當執行放在數組中的某個匿名函數的時候
會複製該函數的[[Scopes]],做爲做用域鏈,
接着創造 該函數的變量對象並把定義在函數內變量和函數放在 變量對象,
把該變量對象推入到當前做用域鏈的最前端
當代碼運行到 console.log(i) 會在當前的做用域的最前端開始查找i
解決方案,我們能夠考慮一下,每次循環的i都放在一個變量對象上面,接下來在建立匿名函數的時候把當前的做用域鏈copy一份放在每一個匿名函數的[[Scopes]],在每一個匿名函數運行的時候,經過做用域鏈查找取得每次寫入到變量對象的i的值
function createFn(){ var result =[]; for(var i=0;i<10;i++){ result[i]=function(num){ return function(){ debugger; console.log(num) } }(i) } return result; } var res=createFn() res[0]() 複製代碼
看圖
接着看下圖就會更明白若是有不足的地方,請你們指出來,我們一塊兒交流,多謝 小夥伴 「Tim在掘金lv-1」 提出這個 for中閉包去i的經典案例