电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

JavaScript閉包原理與用法學習筆記

瀏覽:111日期:2023-10-24 15:50:58

本文實例講述了JavaScript閉包原理與用法。分享給大家供大家參考,具體如下:

閉包(Closure)

閉包是一個函數和詞法環境的組合,函數聲明在這個詞法環境中。

詞法作用域:

看下面的一個例子:

function init() { var name = ’GaoPian’; // name是局部變量 function displayName() { //displayName();是內部函數,一個閉包 alert(name); // 使用外部函數聲明的變量 } displayName(); } init();

init()創建了一個局部變量name和一個函數displayName()。

函數displayName()是一個已經定義在init()內部的函數,并且只能在函數init()里面才能訪問得到。

函數displayName()沒有自己的局部變量,但由于內部函數可以訪問外部函數變量,displayName()可以訪問到聲明在外部函數init()的變量name,如果局部變量還存在的話,displayName()也可以訪問他們。

閉包

看下面一個例子

function makeFunc() { debugger var name = ’GaoPian’; function displayName() { alert(name); } return displayName; } var myFunc = makeFunc(); myFunc();

運行這段代碼和之前init()的方法的效果是一樣。

經過debugger一遍之后發現:

二者不同之處是,displayName()在執行之前,這個內部方法是從外部方法返回來的。   

首先,代碼還是會正確運行,在一些編程語言當中,一個函數內的局部變量只存在于該函數的執行期間,隨后會被銷毀,一旦makeFunc()函數執行完畢的話,變量名就不能夠被獲取,但是,由于代碼仍然正常執行,這顯然在JS里是不會這樣的。這是因為函數在JS里是以閉包的形式出現的。

閉包是一個函數和詞法作環境的組合,詞法環境是函數被聲明的那個作用域,這個執行環境包括了創建閉包時同一創建的任意變量,即創建的這個函數和這些變量處于同一個作用域當中。在這個例子當中,myFunc()是displayName()的函數實例,makeFunc創建的時候,displayName隨之也創建了。displayName的實例可以獲得詞法作用域的引用,在這個詞法作用域當中,存在變量name,對于這一點,當myFunc調用的話,變量name,仍然可以被調用,因此,變量’GaoPian’傳遞給了alert函數。

這里還有一個例子

function makeAdder(x) { return function (y) { return x + y; } } var add5 = makeAdder(5); var add10 = makeAdder(10); console.log(add5(2)); // 7 console.log(add10(2)); // 12

在這個例子當中,我們定義了一個函數makeAdder(x),傳遞一個參數x,并且返回一個函數,這個返回函數接收一個參數y,并返回x和y的和。   

實際上,makeAdder是一個工廠模式:它創建了一個函數,這個函數可以計算特定值的和。在上面這個例子當中,我們使用工廠模式來創建新的函數, 一個與5進行加法運算——add5,一個與10進行加法運算——add10。add5和add10都是閉包,他們共享相同的函數定義,但卻存儲著不同的詞法環境,在add5的詞法環境當中,x為5;在add10的詞法環境當中,x變成了10。

閉包的實踐

閉包是很有用的,因為他讓我們把一些數據(詞法環境)和一些能夠獲取這些數據的函數聯系起來,這有點和面向對象編程類似,在面向對象編程當中,對象讓我們可以把一些數據(對象的屬性)和一個或多個方法聯系起來。

因此,你能夠像對象的方法一樣隨時使用閉包。實際上,大多數的前端JS代碼都是事件驅動性的:我們定義一些事件,當這個事件被用戶所觸發的時候(例如用戶的點擊事件和鍵盤事件),我們的事件通常會帶上一個回調:即事件觸發所執行的函數。舉個栗子,假設我們希望在頁面上添加一些按鈕,這些按鈕能夠調整文字的大小,實現這個功能的方式是確定body的字體大小,然后再設置頁面上其他元素(例如標題)的字體大小,我們使用em作為單位。

<style> body { font-family: Helvetica, Arial, sans-serif; font-size: 12px; } h1 { font-size: 1.5em; } h2 { font-size: 1.2em; } </style>

我們設置的調節字體大小的按鈕能夠改變body的font-size,并且這個調節能夠通過相對字體單位,反應到其他元素上,

function makeSizer(size) { return function () { document.body.style.fontSize = size + ’px’; }; } var size12 = makeSizer(12); var size14 = makeSizer(14); var size16 = makeSizer(16);

size12,size14,size16是三個分別把字體大小調整為12,14,16的函數,我們可以把他們綁定在按鈕上。

<button id='size-12'>12</button><button id='size-14'>14</button><button id='size-16'>16</button>

document.getElementById(’size-12’).onclick = size12; document.getElementById(’size-14’).onclick = size14; document.getElementById(’size-16’).onclick = size16;

通過閉包來封裝私有方法:類似JAVA語言能夠聲明私有方法,意味著只能夠在相同的類里面被調用,JS無法做到這一點,但卻可以通過閉包來封裝私有方法。私有方法不限制代碼:他們提供了管理命名空間的一種強有力方式。

下面代碼闡述了怎樣使用閉包來定義公有函數,公有函數能夠訪問私有方法和屬性。

var counter = (function () { debugger; var privateCounter = 0; function changeBy(val) { privateCounter += val; } return { increment: function () {changeBy(1); }, decrement: function () {changeBy(-1); }, value: function () {return privateCounter; } }; })(); console.log(counter.value());// 0 counter.increment(); counter.increment(); console.log(counter.value());// 2 counter.decrement(); console.log(counter.value()); // 1

在之前的例子當中,每個閉包具有他們自己的詞法環境,而在這個例子中,我們創建了一個單獨的詞法環境,這個詞法環境被3個函數所共享,這三個函數是counter.increment, counter.decrement和counter.value。

共享的詞法環境是由匿名函數創建的,一定義就可以被執行,詞法環境包含兩項:變量privateCounter和函數changeBy,這些私有方法和屬性不能夠被外面訪問到,然而,他們能夠被返回的公共函數訪問到。這三個公有函數就是閉包,共享相同的環境,JS的詞法作用域的好處就是他們可以互相訪問變量privateCounter和changeBy函數。

下面一個例子:

var makeCounter = function () { var privateCounter = 0; function changeBy(val) { privateCounter += val; } return { increment: function () {changeBy(1); }, decrement: function () {changeBy(-1); }, value: function () {return privateCounter; } } }; var counter1 = makeCounter(); var counter2 = makeCounter(); alert(counter1.value()); /* Alerts 0 */ counter1.increment(); counter1.increment(); alert(counter1.value()); /* Alerts 2 */ counter1.decrement(); alert(counter1.value()); /* Alerts 1 */ alert(counter2.value()); /* Alerts 0 */

兩個計數器counter1和counter2分別是互相獨立的,每個閉包具有不同版本的privateCounter,每次計數器被調用,詞法環境會改變變量的值,但是一個閉包里變量值的改變并不影響另一個閉包里的變量。

循環中創建閉包:常見錯誤

看下面一個例子:

<p id='help'>Helpful notes will appear here</p><p>E-mail: <input type='text' name='email'></p><p>Name: <input type='text' name='name'></p><p>Age: <input type='text' name='age'></p>

function showHelp(help) { document.getElementById(’help’).innerHTML = help; } function setupHelp() { var helpText = [{’id’: ’email’, ’help’: ’Your e-mail address’}, {’id’: ’name’, ’help’: ’Your full name’}, {’id’: ’age’, ’help’: ’Your age (you must be over 16)’}]; for (var i = 0; i < helpText.length; i++) { var item = helpText[i]; document.getElementById(item.id).onfocus = function () {showHelp(item.help); } } } setupHelp();

helpText 數組定義了三個有用的hint,每個分別與輸入框的id相對應,每個方法與onfocus事件綁定起來。當你運行這段代碼的時候,不會像預期的那樣工作,不管你聚焦在哪個輸入框,始終顯示你的age信息。

原因在于,分配給onfocus事件的函數是閉包,他們由函數定義構成,從setupHelp函數的函數作用域獲取。三個閉包由循環所創建,每個閉包具有同一個詞法環境,環境中包含一個變量item.help,當onfocus的回調執行時,item.help的值也隨之確定,循環已經執行完畢,item對象已經指向了helpText列表的最后一項。

解決這個問題的方法是使用更多的閉包,具體點就是提前使用一個封裝好的函數:

function showHelp(help) { document.getElementById(’help’).innerHTML = help; } function makeHelpCallback(help) { return function () { showHelp(help); }; } function setupHelp() { var helpText = [{’id’: ’email’, ’help’: ’Your e-mail address’}, {’id’: ’name’, ’help’: ’Your full name’}, {’id’: ’age’, ’help’: ’Your age (you must be over 16)’}]; for (var i = 0; i < helpText.length; i++) { var item = helpText[i]; document.getElementById(item.id).onfocus = makeHelpCallback(item.help); } } setupHelp();

上面代碼運行正常,回調此時不共享一個詞法環境,makeHelpCallback函數給每個回調創造了一個詞法環境,詞法環境中的help指helpText數組中對應的字符串,使用匿名閉包來重寫的例子如下:

function showHelp(help) { document.getElementById(’help’).innerHTML = help; } function setupHelp() { var helpText = [{’id’: ’email’, ’help’: ’Your e-mail address’}, {’id’: ’name’, ’help’: ’Your full name’}, {’id’: ’age’, ’help’: ’Your age (you must be over 16)’}]; for (var i = 0; i < helpText.length; i++) { (function () {var item = helpText[i];document.getElementById(item.id).onfocus = function () { showHelp(item.help);} })(); // Immediate event listener attachment with the current value of item (preserved until iteration). } } setupHelp();

如果不想使用閉包,也可以使用ES6的let關鍵字:

function showHelp(help) { document.getElementById(’help’).innerHTML = help; } function setupHelp() { var helpText = [{’id’: ’email’, ’help’: ’Your e-mail address’}, {’id’: ’name’, ’help’: ’Your full name’}, {’id’: ’age’, ’help’: ’Your age (you must be over 16)’}]; for (var i = 0; i < helpText.length; i++) { let item = helpText[i]; document.getElementById(item.id).onfocus = function () {showHelp(item.help); } } } setupHelp();

這個例子使用let代替var,所以,每個閉包綁定了塊級作用域,也就意味著不需要額外的閉包。

感興趣的朋友可以使用在線HTML/CSS/JavaScript代碼運行工具:http://tools.jb51.net/code/HtmlJsRun測試上述代碼運行效果。

更多關于JavaScript相關內容感興趣的讀者可查看本站專題:《javascript面向對象入門教程》、《JavaScript錯誤與調試技巧總結》、《JavaScript數據結構與算法技巧總結》、《JavaScript遍歷算法與技巧總結》及《JavaScript數學運算用法總結》

希望本文所述對大家JavaScript程序設計有所幫助。

標簽: JavaScript
相關文章:
主站蜘蛛池模板: 户外环保不锈钢垃圾桶_标识标牌制作_园林公园椅厂家_花箱定制-北京汇众环艺 | 微量水分测定仪_厂家_卡尔费休微量水分测定仪-淄博库仑 | 细沙回收机-尾矿干排脱水筛设备-泥石分离机-建筑垃圾分拣机厂家-青州冠诚重工机械有限公司 | 北京企业宣传片拍摄_公司宣传片制作-广告短视频制作_北京宣传片拍摄公司 | 山东成考网-山东成人高考网| 非小号行情 - 专业的区块链、数字藏品行情APP、金色财经官网 | 宽带办理,电信宽带,移动宽带,联通宽带,电信宽带办理,移动宽带办理,联通宽带办理 | 招商帮-一站式网络营销服务|互联网整合营销|网络推广代运营|信息流推广|招商帮企业招商好帮手|搜索营销推广|短视视频营销推广 | 防水套管厂家-柔性防水套管-不锈钢|刚性防水套管-天翔管道 | 节流截止放空阀-不锈钢阀门-气动|电动截止阀-鸿华阀门有限公司 | GEDORE扭力螺丝刀-GORDON防静电刷-CHEMTRONICS吸锡线-上海卓君电子有限公司 | 东莞爱加真空科技有限公司-进口真空镀膜机|真空镀膜设备|Polycold维修厂家 | 智能电表|预付费ic卡水电表|nb智能无线远传载波电表-福建百悦信息科技有限公司 | 深圳离婚律师咨询「在线免费」华荣深圳婚姻律师事务所专办离婚纠纷案件 | 沈阳真空机_沈阳真空包装机_沈阳大米真空包装机-沈阳海鹞真空包装机械有限公司 | 浙江红酒库-冰雕库-气调库-茶叶库安装-医药疫苗冷库-食品物流恒温恒湿车间-杭州领顺实业有限公司 | 吸污车_吸粪车_抽粪车_电动三轮吸粪车_真空吸污车_高压清洗吸污车-远大汽车制造有限公司 | LINK FASHION 童装·青少年装展 河南卓美创业科技有限公司-河南卓美防雷公司-防雷接地-防雷工程-重庆避雷针-避雷器-防雷检测-避雷带-避雷针-避雷塔、机房防雷、古建筑防雷等-山西防雷公司 | 深圳高新投三江工业消防解决方案提供厂家_服务商_园区智慧消防_储能消防解决方案服务商_高新投三江 | 北京三友信电子科技有限公司-ETC高速自动栏杆机|ETC机柜|激光车辆轮廓测量仪|嵌入式车道控制器 | 并离网逆变器_高频UPS电源定制_户用储能光伏逆变器厂家-深圳市索克新能源 | 橡胶接头|可曲挠橡胶接头|橡胶软接头安装使用教程-上海松夏官方网站 | 杭州代理记账多少钱-注册公司代办-公司注销流程及费用-杭州福道财务管理咨询有限公司 | 台湾阳明固态继电器-奥托尼克斯光电传感器-接近开关-温控器-光纤传感器-编码器一级代理商江苏用之宜电气 | 冷却塔改造厂家_不锈钢冷却塔_玻璃钢冷却塔改造维修-广东特菱节能空调设备有限公司 | 喷漆房_废气处理设备-湖北天地鑫环保设备有限公司 | 同步带轮_同步带_同步轮_iHF合发齿轮厂家-深圳市合发齿轮机械有限公司 | 成都LED显示屏丨室内户外全彩led屏厂家方案报价_四川诺显科技 | 济南玻璃安装_济南玻璃门_济南感应门_济南玻璃隔断_济南玻璃门维修_济南镜片安装_济南肯德基门_济南高隔间-济南凯轩鹏宇玻璃有限公司 | 测试治具|过炉治具|过锡炉治具|工装夹具|测试夹具|允睿自动化设备 | 医养体检包_公卫随访箱_慢病随访包_家签随访包_随访一体机-济南易享医疗科技有限公司 | 承插管件_不锈钢承插管件_锻钢高压管件-温州科正阀门管件有限公司 | TPU薄膜_TPU薄膜生产厂家_TPU热熔胶膜厂家定制_鑫亘环保科技(深圳)有限公司 | 振动筛-交叉筛-螺旋筛-滚轴筛-正弦筛-方形摇摆筛「新乡振动筛厂家」 | 活性炭-蜂窝-椰壳-柱状-粉状活性炭-河南唐达净水材料有限公司 | 技德应用| 全自动端子机|刺破式端子压接机|全自动双头沾锡机|全自动插胶壳端子机-东莞市傅氏兄弟机械设备有限公司 | 光栅尺厂家_数显表维修-苏州泽升精密机械 | 江西自考网-江西自学考试网| 回转炉,外热式回转窑,回转窑炉-淄博圣元窑炉工程有限公司 | 镀锌钢格栅_热镀锌格栅板_钢格栅板_热镀锌钢格板-安平县昊泽丝网制品有限公司 |