學(xué)習(xí)JavaScript中的閉包closure應(yīng)該注意什么
閉包簡述
Mozilla 上這樣解釋閉包:一個函數(shù)和對其周圍狀態(tài)(lexical environment,詞法環(huán)境) 的引用捆綁在一起(或者說函數(shù)被引用包圍),這樣的組合就是閉包(closure)。 也就是說,閉包讓你可以在一個內(nèi)層函數(shù)中訪問到其外層函數(shù)的作用域。在 JavaScript 中, 每當創(chuàng)建一個函數(shù), 閉包就會在函數(shù)創(chuàng)建的同時被創(chuàng)建出來。 詞法(lexical)一詞指的是,詞法作用域根據(jù)源代碼中聲明變量的位置來確定該變量在何處可用。
我對閉包的理解:閉包使得可以模擬私有項,可以使得內(nèi)部函數(shù)可以訪問外部函數(shù)的屬性,非必要不用閉包。
1.閉包使得內(nèi)部函數(shù)可以訪問外部函數(shù)的屬性(變量或方法)
這有時會帶來便利, 例如有時可以通過在外部函數(shù)聲明變量,代替全局變量。 下面是一個設(shè)備視口大小改變時,重置 echarts 的例子。
// 設(shè)備視口大小改變時,重置 echarts let timer = null window.onresize = function () { // 簡單的防抖動處理 if (timer) clearTimeout(timer) timer = setTimeout(() => { console.log(timer) chart.resize() }, 500) }
也可以考慮使用閉包的方式,而不必在聲明全局變量(更大范圍的變量) timer,例如這樣
window.onresize = this.debounce(() => { chart.resize() }, 500) function debounce (fn, delay = 500) { let timer = null return (p) => { if (timer) clearTimeout(timer) timer = setTimeout(() => { fn(p) }, delay) } }
2.閉包的廣闊應(yīng)用場景
閉包的廣闊應(yīng)用場景,體現(xiàn)在你使用只有一個方法的對象的地方,都可以使用閉包。
因為閉包允許將函數(shù)與其所操作的某些數(shù)據(jù)(環(huán)境)關(guān)聯(lián)起來。這顯然類似于面向?qū)ο缶幊獭?在面向?qū)ο缶幊讨校瑢ο笤试S我們將某些數(shù)據(jù)(對象的屬性)與一個或者多個方法相關(guān)聯(lián)。
而在日常開發(fā)中,符合使用閉包的場景其實很常見,因為使用只有一個方法的對象的地方,都可以使用閉包, 而使用也并不太麻煩,加上閉包本身就是 javascript 的重要知識點,這些加起來使得閉包具備了實用的特征。
但如果你不熟練閉包,有更好的替代方案,也不必非要使用,因為實用好用的東西很多, 閉包只是選擇之一,為了給自己多一種選擇閉包又是要學(xué)的。
3.用閉包模擬私有方法
JavaScript 沒有類似 JAVA 那樣的將方法聲明為私有的原生支持,但我們可以使用閉包來模擬私有方法。 私有方法不僅僅有利于限制對代碼的訪問,還提供了管理全局命名空間的強大能力, 避免非核心的方法弄亂代碼的公共接口部分。
下面的示例展現(xiàn)了如何使用閉包來定義公共函數(shù),并令其可以訪問私有函數(shù)和變量。這個方式也稱為模塊模式(module pattern)
window.onload = () => { let Counter1 = makeCounter(); // 創(chuàng)建實例1 let Counter2 = makeCounter(); // 創(chuàng)建實例2 console.log(Counter1.value()); // value:0 Counter1.add(); // 調(diào)用增加函數(shù),執(zhí)行加一 console.log(Counter1.value()); // value:1 console.log(Counter2.value()); // value:0 // 注意,實例2的 value 沒有受到實例1的影響,也就是說 Counter1 和 Counter2 各自獨立。 // 每次調(diào)用其中一個計數(shù)器時,通過改變這個變量的值,會改變這個閉包的詞法環(huán)境。 // 然而在一個閉包內(nèi)對變量的修改,不會影響到另外一個閉包中的變量。 // undefined,Counter1 無法直接訪問私有項 privateNumber console.log(Counter1.privateNumber); // Counter1.changeBy is not a function,Counter1 無法直接訪問私有項 changeBy console.log(Counter1.changeBy(10)); // 問私有項無法被訪問,這提示我們應(yīng)關(guān)注到以這種方式使用閉包, // 提供了許多與面向?qū)ο缶幊滔嚓P(guān)的好處 —— 特別是數(shù)據(jù)隱藏和封裝。 } /// 聲明一個模塊:計數(shù)器,模塊內(nèi)部包含了兩個模擬的私有項 privateNumber 和 changeBy, // 并返回一個對象,對象內(nèi)部包含三個屬性,分別是 add(),reduce(),value()。 let makeCounter = function () { let privateNumber = 0; function changeBy (val) { privateNumber += val; } return { add: function () { changeBy(1); }, reduce: function () { changeBy(-1); }, value: function () { return privateNumber; } } };
在這個例子中,包含兩個私有項: 名為 privateCounter 的變量和名為 changeBy 的函數(shù)。 這兩項都無法在函數(shù)外部直接訪問。必須通過匿名函數(shù)返回的三個公共函數(shù)訪問。這就是模擬了私有特性。
4.從性能角度考慮,非必要不使用閉包
關(guān)于閉包的性能,我無深入的理解,也無數(shù)據(jù)證明,但我認為這挺重要的。
因此,這里引用一下 Mozilla 的說法:
如果不是某些特定任務(wù)需要使用閉包,在其它函數(shù)中創(chuàng)建函數(shù)是不明智的, 因為閉包在處理速度和內(nèi)存消耗方面對腳本性能具有負面影響。
例如,在創(chuàng)建新的對象或者類時,方法通常應(yīng)該關(guān)聯(lián)于對象的原型,而不是定義到對象的構(gòu)造器中。 原因是這將導(dǎo)致每次構(gòu)造器被調(diào)用時,方法都會被重新賦值一次(也就是說,對于每個對象的創(chuàng)建,方法都會被重新賦值)。
英文原文:
It is unwise to unnecessarily create functions within other functions if closures are not needed for a particular task, as it will negatively affect script performance both in terms of processing speed and memory consumption.
For instance, when creating a new object/class, methods should normally be associated to the object's prototype rather than defined into the object constructor. The reason is that whenever the constructor is called, the methods would get reassigned (that is, for every object creation).
概括就是一句話,非必要不用閉包。好東西很多閉包只是之一, 當然閉包作為js的重要知識點,作為可能的解決方案之一,學(xué)習(xí)是必要的。
到此這篇關(guān)于學(xué)習(xí)JavaScript中的閉包closure應(yīng)該注意什么的文章就介紹到這了,更多相關(guān)JS 閉包closuret內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript?中?this?關(guān)鍵字的作用及改變其上下文的方法
這篇文章主要介紹了JavaScript?中?this?關(guān)鍵字的作用和如何改變其上下文,通過使用?call,?apply,?bind?方法,可以改變函數(shù)中的?this?指向,從而在不同的上下文中使用同一個函數(shù),需要的朋友可以參考下2023-01-01JavaScript-定時器0~9抽獎系統(tǒng)詳解(代碼)
這篇文章主要介紹了 JavaScript-定時器0~9抽獎系統(tǒng),通過代碼實例說明函數(shù)調(diào)用的整體操作,具體步驟大家可查看下文的詳細講解,感興趣的小伙伴們可以參考一下。2017-08-08