javascript
解析面试常问题之JavaScript中的闭包概念及应用,顺便普及一下大家口中常说的内存泄漏问题
JavaScript中的閉包是一個面試中經常被考到的問題,大家可能都對這個概念多多少少都有一些模糊的概念或者一點都不了解,那么今天就來給大家講解一下。
- 公眾號:前端印象
- 不定時有送書活動,記得關注~
- 關注后回復對應文字領取:【面試題】、【前端必看電子書】、【數據結構與算法完整代碼】、【前端技術交流群】
JavaScript之閉包
- 一、引言
- 二、閉包的定義
- 三、體驗閉包
- 三、使用閉包的注意事項
- 四、內存泄漏
- 五、閉包的私有變量
- 六、總結
- 七、結束語
一、引言
首先在這里我得說一下,要了解閉包一定要有作用域鏈的相關概念,這里我放上一篇文章,希望大家花3分鐘看一下,了解一下作用域鏈,否則后面看起來會有點懵。作用域鏈講解文章——從零開始講解JavaScript中作用域鏈的概念及用途
二、閉包的定義
閉包: 是指有權訪問另一個函數作用中的變量的函數,常見的閉包形式就是一個函數的內部再創建另一個函數。
想必這個概念聽起來很懵,那我們接下來就來體驗一個閉包吧。
三、體驗閉包
來用一個小案例,來體驗一下閉包是什么
<!DOCTYPE html> <html> <head><meta charset="UTF-8"><title></title> </head><body><div class="show">0</div> <button>增加</button><script>let show = document.querySelector('.show')let btn = document.querySelector('button')function func() {let n = 0btn.onclick = function () {n ++show.innerHTML = n}}f() </script></body> </html>接下來是gif動圖展示
我們可以看到,在函數 fnc 中定義了一個變量 n,而我們在該函數內部,對按鈕 btn 綁定了一個點擊函數,該點擊函數將變量 n + 1,然后展示在頁面上。了解過作用域鏈的小伙伴會知道,當我們點擊按鈕的時候,處罰了點擊的處理函數,此時該函數內部的作用域鏈是這個樣子的
引用了變量 n ,首先在作用域鏈的第一個變量對象中尋找變量 n,沒有找到;然后去作用域鏈的第二個里尋找變量 n,找到了,也就是在函數 func 內部定義的變量 n。所以該點擊處理函數每次引用變量 n 時,都是從函數 func 內部去尋找的變量 n ,這也就是我們所說的,一個函數有權訪問另一個函數內部的變量。
三、使用閉包的注意事項
上面我們了解了閉包的基本使用,那么我們再用一個例子來給大家介紹在使用閉包時容易犯的錯誤。
function create() {var arr = []for(var i=0; i<10; i++) {arr[i] = function () {return i}}return arr }let result = create()result[0]() //返回10 result[1]() //返回10 …… result[9]() //返回10這個例子就是在函數 create 中通過 for 循環定義10個匿名函數,每個函數都返回變量 i,最終將每個匿名函數保存到數組 arr 中并返回數組 arr,然后我們在收到數組 arr 后依次調用每個匿名函數,發現每個返回的都是數字10,而我們最初的目的是依次返回的是 0~9。
這是因為,我們調用匿名函數的時候需要返回變量 i ,而匿名函數內部沒有該變量,所以去往下一個變量對象,也就是定義匿名函數時所處的函數環境 create 中尋找變量 i ,但此時的變量 i 已經通過循環變成了10,所以當我們調用每個匿名函數時,返回的全部都是10.
為此,我們的代碼可以寫成這樣
function create() {var arr = []for(var i=0; i<10; i++) {arr[i] = (function (num) {return function () {return num}})(i)}return arr }let result = create()result[0]() //返回 0 result[1]() //返回 1 …… result[9]() //返回 9這樣做就直接在定義最內部的匿名函數時,把當前循環的變量 i 放在了最內部匿名函數外部的那個匿名函數內,這樣的話,我們之后調用匿名函數時,尋找變量 i 就會從該匿名函數外部的那個匿名函數的變量對象中找到相應的變量。
四、內存泄漏
相信面試過的小伙伴都知道,在面試時,如果面試官問到你閉包,可能會跟你提一下內存泄漏。
首先我要打假一個說法,很多人都說閉包會引起內存泄漏,這一半真一半假,因為只有在IE9之前才會因為閉包出現內存泄露的問題,所以以后千萬別在別人面前說閉包就會引起內存泄露了哈。
那么內存泄露到底是什么呢?
簡單來說一下,就是當一個閉包的作用域鏈內有一個HTML元素時,該元素就無法被垃圾回收機制給銷毀。
這里不懂JavaScript垃圾回收機制的小伙伴可以花2分鐘看一下這篇文章,下面會講解到,以防聽不懂——JavaScript的垃圾回收機制,清除無用變量,釋放多余內存,展現更好的性能
function handle() {let element = document.querySelector('#app')element.onclick = function () {console.log(element.id)} }在函數 handle 中,給HTML元素 element 創建了一個點擊事件的匿名函數,該函數內部引用了變量 element ,所以變量 element 的引用次數為1,這樣的話垃圾回收機制一直都不會清除該元素了,這就是一個內存泄露的情況。
所以我們可以這樣做,來解決內存泄露的問題
function handle() {let element = document.querySelector('#app')let id = element.idelement.onclick = function () {console.log(id)}element = null }將元素 element 的 id 值保存在一個變量 id 內,然后在該元素的點擊處理事件中引用變量 id , 并且在最后通過把變量 element設置為 null ,以解除對DOM元素的引用,這樣引用次數就變為0,而不再是1了,垃圾回收機制就可以對其進行清除了。
五、閉包的私有變量
顧名思義,私有變量的意思就是說,閉包擁有自己的變量,別人都無法訪問,無法使用。
很明顯,了解過作用域鏈就能清楚得知道,當函數調用后,作用域鏈是先從最內部開始,然后向外依次排列。所以只有內部訪問外部變量的說法,而沒有說外部訪問內部變量的道理。
就比如這個簡單的例子
let m = 1 let n = 4function func() {let n = 2alert(m) //返回 1return function () {let m = 3alert(n) //返回 2} }func()在該例子中可以看到,函數 func 本意想訪問匿名函數中的變量 m 值為3,但卻只訪問到全局中的變量 m 值為1;而匿名函數就成功訪問到了函數 func 內部定義的變量 n 值為2
這就是通過閉包實現的私有變量的例子
六、總結
- 閉包就是指有權訪問另一個函數作用中的變量的函數,常見的閉包形式就是一個函數的內部再創建另一個函數。
- 閉包就是為了隱藏變量,使外部無法訪問到
- 閉包可以將變量定義在內部,使內部擁有自己的變量,同時可以不污染全局變量
七、結束語
想必這篇文章應該能讓你對閉包的概念有了很深的理解了。
希望這篇文章能對你們有所幫助,我是Lpyexplore,創作不易,喜歡的加個關注,點個收藏,給個贊~
總結
以上是生活随笔為你收集整理的解析面试常问题之JavaScript中的闭包概念及应用,顺便普及一下大家口中常说的内存泄漏问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用电脑却要安装Linux系统的五个理由
- 下一篇: 利用VirtualBox搭建私有云