學(xué)習(xí)JavaScript中的閉包c(diǎn)losure應(yīng)該注意什么
閉包簡(jiǎn)述
Mozilla 上這樣解釋閉包:一個(gè)函數(shù)和對(duì)其周圍狀態(tài)(lexical environment,詞法環(huán)境) 的引用捆綁在一起(或者說(shuō)函數(shù)被引用包圍),這樣的組合就是閉包(closure)。 也就是說(shuō),閉包讓你可以在一個(gè)內(nèi)層函數(shù)中訪問(wèn)到其外層函數(shù)的作用域。在 JavaScript 中, 每當(dāng)創(chuàng)建一個(gè)函數(shù), 閉包就會(huì)在函數(shù)創(chuàng)建的同時(shí)被創(chuàng)建出來(lái)。 詞法(lexical)一詞指的是,詞法作用域根據(jù)源代碼中聲明變量的位置來(lái)確定該變量在何處可用。
我對(duì)閉包的理解:閉包使得可以模擬私有項(xiàng),可以使得內(nèi)部函數(shù)可以訪問(wèn)外部函數(shù)的屬性,非必要不用閉包。
1.閉包使得內(nèi)部函數(shù)可以訪問(wèn)外部函數(shù)的屬性(變量或方法)
這有時(shí)會(huì)帶來(lái)便利, 例如有時(shí)可以通過(guò)在外部函數(shù)聲明變量,代替全局變量。 下面是一個(gè)設(shè)備視口大小改變時(shí),重置 echarts 的例子。
// 設(shè)備視口大小改變時(shí),重置 echarts
let timer = null
window.onresize = function () {
// 簡(jiǎn)單的防抖動(dòng)處理
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)用場(chǎng)景
閉包的廣闊應(yīng)用場(chǎng)景,體現(xiàn)在你使用只有一個(gè)方法的對(duì)象的地方,都可以使用閉包。
因?yàn)殚]包允許將函數(shù)與其所操作的某些數(shù)據(jù)(環(huán)境)關(guān)聯(lián)起來(lái)。這顯然類似于面向?qū)ο缶幊獭?在面向?qū)ο缶幊讨?,?duì)象允許我們將某些數(shù)據(jù)(對(duì)象的屬性)與一個(gè)或者多個(gè)方法相關(guān)聯(lián)。
而在日常開發(fā)中,符合使用閉包的場(chǎng)景其實(shí)很常見,因?yàn)槭褂弥挥幸粋€(gè)方法的對(duì)象的地方,都可以使用閉包, 而使用也并不太麻煩,加上閉包本身就是 javascript 的重要知識(shí)點(diǎn),這些加起來(lái)使得閉包具備了實(shí)用的特征。
但如果你不熟練閉包,有更好的替代方案,也不必非要使用,因?yàn)閷?shí)用好用的東西很多, 閉包只是選擇之一,為了給自己多一種選擇閉包又是要學(xué)的。
3.用閉包模擬私有方法
JavaScript 沒(méi)有類似 JAVA 那樣的將方法聲明為私有的原生支持,但我們可以使用閉包來(lái)模擬私有方法。 私有方法不僅僅有利于限制對(duì)代碼的訪問(wèn),還提供了管理全局命名空間的強(qiáng)大能力, 避免非核心的方法弄亂代碼的公共接口部分。
下面的示例展現(xiàn)了如何使用閉包來(lái)定義公共函數(shù),并令其可以訪問(wèn)私有函數(shù)和變量。這個(gè)方式也稱為模塊模式(module pattern)
window.onload = () => {
let Counter1 = makeCounter(); // 創(chuàng)建實(shí)例1
let Counter2 = makeCounter(); // 創(chuàng)建實(shí)例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
// 注意,實(shí)例2的 value 沒(méi)有受到實(shí)例1的影響,也就是說(shuō) Counter1 和 Counter2 各自獨(dú)立。
// 每次調(diào)用其中一個(gè)計(jì)數(shù)器時(shí),通過(guò)改變這個(gè)變量的值,會(huì)改變這個(gè)閉包的詞法環(huán)境。
// 然而在一個(gè)閉包內(nèi)對(duì)變量的修改,不會(huì)影響到另外一個(gè)閉包中的變量。
// undefined,Counter1 無(wú)法直接訪問(wèn)私有項(xiàng) privateNumber
console.log(Counter1.privateNumber);
// Counter1.changeBy is not a function,Counter1 無(wú)法直接訪問(wèn)私有項(xiàng) changeBy
console.log(Counter1.changeBy(10));
// 問(wèn)私有項(xiàng)無(wú)法被訪問(wèn),這提示我們應(yīng)關(guān)注到以這種方式使用閉包,
// 提供了許多與面向?qū)ο缶幊滔嚓P(guān)的好處 —— 特別是數(shù)據(jù)隱藏和封裝。
}
/// 聲明一個(gè)模塊:計(jì)數(shù)器,模塊內(nèi)部包含了兩個(gè)模擬的私有項(xiàng) privateNumber 和 changeBy,
// 并返回一個(gè)對(duì)象,對(duì)象內(nèi)部包含三個(gè)屬性,分別是 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;
}
}
};在這個(gè)例子中,包含兩個(gè)私有項(xiàng): 名為 privateCounter 的變量和名為 changeBy 的函數(shù)。 這兩項(xiàng)都無(wú)法在函數(shù)外部直接訪問(wèn)。必須通過(guò)匿名函數(shù)返回的三個(gè)公共函數(shù)訪問(wèn)。這就是模擬了私有特性。
4.從性能角度考慮,非必要不使用閉包
關(guān)于閉包的性能,我無(wú)深入的理解,也無(wú)數(shù)據(jù)證明,但我認(rèn)為這挺重要的。
因此,這里引用一下 Mozilla 的說(shuō)法:
如果不是某些特定任務(wù)需要使用閉包,在其它函數(shù)中創(chuàng)建函數(shù)是不明智的, 因?yàn)殚]包在處理速度和內(nèi)存消耗方面對(duì)腳本性能具有負(fù)面影響。
例如,在創(chuàng)建新的對(duì)象或者類時(shí),方法通常應(yīng)該關(guān)聯(lián)于對(duì)象的原型,而不是定義到對(duì)象的構(gòu)造器中。 原因是這將導(dǎo)致每次構(gòu)造器被調(diào)用時(shí),方法都會(huì)被重新賦值一次(也就是說(shuō),對(duì)于每個(gè)對(duì)象的創(chuàng)建,方法都會(huì)被重新賦值)。
英文原文:
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).
概括就是一句話,非必要不用閉包。好東西很多閉包只是之一, 當(dāng)然閉包作為js的重要知識(shí)點(diǎn),作為可能的解決方案之一,學(xué)習(xí)是必要的。
到此這篇關(guān)于學(xué)習(xí)JavaScript中的閉包c(diǎn)losure應(yīng)該注意什么的文章就介紹到這了,更多相關(guān)JS 閉包c(diǎn)losuret內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Bootstrap中的fileinput 多圖片上傳及編輯功能
這篇文章主要介紹了Bootstrap中的fileinput 多圖片上傳及編輯功能的實(shí)現(xiàn),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-09-09
Bootstrap datepicker日期選擇器插件使用詳解
這篇文章主要為大家詳細(xì)介紹了Bootstrap datepicker日期選擇器插件的使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07
JS實(shí)現(xiàn)隨機(jī)亂撞彩色圓球特效的方法
這篇文章主要介紹了JS實(shí)現(xiàn)隨機(jī)亂撞彩色圓球特效的方法,可實(shí)現(xiàn)彩色小球的碰撞效果,涉及隨機(jī)函數(shù)與頁(yè)面樣式的操作技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-05-05
Bootstrap基本布局實(shí)現(xiàn)方法詳解
這篇文章主要為大家詳細(xì)介紹了Bootstrap基本布局實(shí)現(xiàn)方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11
JS實(shí)現(xiàn)select選中option觸發(fā)事件操作示例
這篇文章主要介紹了JS實(shí)現(xiàn)select選中option觸發(fā)事件操作,結(jié)合實(shí)例形式總結(jié)分析了javascript針對(duì)select下拉選中option項(xiàng)觸發(fā)事件相關(guān)操作技巧,需要的朋友可以參考下2018-07-07
javascript中的startWith和endWith的幾種實(shí)現(xiàn)方法
javascript中的startWith和endWith的幾種實(shí)現(xiàn)方法,需要的朋友可以參考一下2013-05-05
微信小程序車牌號(hào)碼模擬鍵盤輸入功能的實(shí)現(xiàn)代碼
這篇文章主要介紹了微信小程序車牌號(hào)碼模擬鍵盤輸入功能的實(shí)現(xiàn)代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-11-11

