日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

JavaScript夯实基础系列(二):闭包

發(fā)布時間:2025/6/17 javascript 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JavaScript夯实基础系列(二):闭包 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

??在JavaScript中函數(shù)是一等公民。所謂一等公民是指函數(shù)跟其他對象一樣,很普通,可以進行把函數(shù)存在數(shù)組中、作為參數(shù)傳遞、賦值給變量等操作。當函數(shù)作為另一個函數(shù)的返回值在外部調(diào)用時,跟該函數(shù)在函數(shù)內(nèi)部調(diào)用時可訪問的詞法作用域一樣,這種現(xiàn)象被稱為閉包。

一、什么是閉包

??閉包的定義有很多,比如:閉包是指有權(quán)訪問另外一個函數(shù)作用域中變量的函數(shù)。或者更本質(zhì)的定義:函數(shù)對象可以通過作用域鏈關(guān)聯(lián)起來,函數(shù)體內(nèi)部的變量都可以保存在函數(shù)作用域內(nèi),也就是說函數(shù)變量可以隱藏于作用域鏈之內(nèi),看上去是函數(shù)將變量“包裹”了起來。個人比較傾向于一種通俗的定義:閉包就是函數(shù)能夠記住并訪問它的詞法作用域,即使當這個函數(shù)在它的詞法作用域外執(zhí)行時。
??如下代碼所示,執(zhí)行test函數(shù)返回print函數(shù),在全局作用域下執(zhí)行print函數(shù),print函數(shù)卻能記住自己的作用域,能夠引用其在定義時的外層函數(shù)test的局部變量。

var a = 1;function test (){var a = 2;function print (){console.log(a)}return print }test()() // 2 復制代碼

??由于JavaScript中沒有塊級作用域的概念,因此常常用立即執(zhí)行函數(shù)(IIFE)來模擬塊級作用域。

var a = 1;(function IIFE(){var a = 2;console.log( a ); // 2})();console.log( a ); // 1 復制代碼

??從學術(shù)意義上來講,JavaScript中的每個函數(shù)都是閉包:它們都是對象,它們都關(guān)聯(lián)到作用域鏈。但是從閉包可以在詞法作用域外調(diào)用也能訪問詞法作用域的角度來說,IIFE并不是閉包。如下代碼所示:在函數(shù)test執(zhí)行時,查找變量a是沿著作用域鏈逐級查詢的,并不能體現(xiàn)閉包的特性。

(function IIFE(){var a = 1;function test () {console.log(a)}test() // 1 })(); 復制代碼

二、閉包的原理

??當某個函數(shù)執(zhí)行時,先復制其外層函數(shù)的作用域鏈(如果函數(shù)是在全局環(huán)境中則作用域鏈中只有一個全局對象的引用),賦值給一個特殊的內(nèi)部屬性(即[[Scope]])。然后使用this、arguments和其它命名參數(shù)的值來初始化函數(shù)的變量對象,最后將該變量對象的引用加入到該函數(shù)的作用域鏈中。
??當函數(shù)執(zhí)行完之后,函數(shù)的作用域鏈上的會被刪除,相應的變量對象沒有了作用域鏈的引用就會被當做垃圾回收掉。但是閉包的情況卻不一樣,函數(shù)雖然執(zhí)行完畢,但是函數(shù)返回了一個內(nèi)部函數(shù)出去,該內(nèi)部函數(shù)的作用域鏈上擁有對該函數(shù)變量對象的引用,因此函數(shù)雖然執(zhí)行完畢,但該函數(shù)的變量對象并沒有被銷毀,依然可以通過返回的內(nèi)部函數(shù)來訪問該函數(shù)變量對象上的變量。
??需要特別注意的是:閉包只能取得包含函數(shù)中任何變量的最后一個值。在for循環(huán)中定義函數(shù)表達式尤其能體現(xiàn)出這一點。如下代碼所示,我們希望test函數(shù)返回的函數(shù)數(shù)組中存放的是可以打印其下標的函數(shù),但是結(jié)果卻是全部數(shù)字10。原因在于我們錯誤的認為每次循環(huán)時都會對i進行一次復制,事實上嵌套的函數(shù)不會將作用域內(nèi)的私有成員復制一份,也不會對所綁定的變量生成靜態(tài)快照。test函數(shù)返回的函數(shù)數(shù)組中引用的都是同一個變量i,變量i被共享,循環(huán)結(jié)束時i的值為10,所以執(zhí)行函數(shù)數(shù)組中的任意函數(shù)結(jié)果都是打印出數(shù)字10。

function test () {var arr = []for(var i=0;i<10;i++){arr[i] = function () {console.log(i)}}return arr } var print = test() print[2]() // 10 復制代碼

??我們對代碼加以改進,來避免數(shù)據(jù)共享的情況發(fā)生。在下面代碼中,并不是直接將閉包賦值給數(shù)組,而是定義了函數(shù)temp,將執(zhí)行temp函數(shù)后的返回值賦給數(shù)組。因為函數(shù)參數(shù)是按值傳遞的,所以每次調(diào)用temp時會復制一份實參i的副本,函數(shù)數(shù)組中保存的函數(shù)都有各自的變量i不同時間段的副本,打破了原本共享數(shù)據(jù)i的情況,因此能夠返回各自不同的值。

function test () {var arr = []for(var i=0;i<10;i++){function temp (j) {return function () {console.log(j)}}arr[i] = temp(i)}return arr } var print = test() print[2]() // 2 復制代碼

三、閉包的用途

??閉包在JavaScript代碼中無所不在,主要應用于模塊模式以及函數(shù)式編程中的柯里化

1、模塊模式

??在ES6之前,JavaScript中并沒有定義用以支持模塊的語言結(jié)構(gòu),但是可以利用閉包很輕松的實現(xiàn)代碼模塊化。在函數(shù)中定義的變量是函數(shù)私有的,在函數(shù)之外不能直接訪問以及修改函數(shù)內(nèi)部的變量,但是通過函數(shù)返回的內(nèi)部函數(shù)能夠訪問這些變量,返回的內(nèi)部函數(shù)如同暴露在外界的共有接口一樣,這種模式被稱為模塊模式。如下代碼所示:

function module () {var value = 0function get () {return value}function set (val) {value = val}return {get: get,set: set} }var test = module() var modu = module() console.log(test.get()) // 0 test.set(1) console.log(modu.get()) // 0 console.log(test.get()) // 1 test.set(10) console.log(modu.get()) // 0 console.log(test.get()) // 10 復制代碼

??在模塊模式中,模塊返回值可以是一個對象,也可以僅僅是一個內(nèi)部函數(shù)。模塊只是一個函數(shù),所以它可以接收參數(shù)。從上面的代碼可以看出函數(shù)每次執(zhí)行返回的閉包是獨立的,相互不影響。一般模塊在使用的時候采用單例模式,可以用IIFE來實現(xiàn),如下代碼所示:

var module =(function module () {var value = 0function get () {return value}function set (val) {value = val}return {get: get,set: set}} )()console.log(module.get()) // 0 module.set(1) console.log(module.get()) // 1 module.set(10) console.log(module.get()) // 10 復制代碼

??綜上所述,模塊要求兩個關(guān)鍵性質(zhì):1、作為模塊的函數(shù)被調(diào)用執(zhí)行。2、該函數(shù)的返回值至少用于一個內(nèi)部函數(shù)的引用。

2、柯里化

??柯里化是指把接受多個參數(shù)的函數(shù)變換成接受一個單一參數(shù)(最初函數(shù)的第一個參數(shù))的函數(shù),并且返回接受余下的參數(shù)而且返回結(jié)果的新函數(shù)的技術(shù)。柯里化的好處在于提高了適用性,能夠?qū)崿F(xiàn)參數(shù)復用的效果。如下代碼所示:

function test (a,b,c) {return a+b+c } console.log(test(1,2,3)) // 6function _test (a) {return function (b) {return function (c) {return a+b+c}} } console.log(_test(1)(2)(3)) // 6 復制代碼

??_test函數(shù)利用函數(shù)閉包來實現(xiàn)柯里化的效果,每次調(diào)用的函數(shù)閉包能夠訪問上次傳入的參數(shù)并訪問。例如lodash等庫封裝了通用柯里化的函數(shù),傳入一個普通函數(shù),返回一個同等功能的柯里化函數(shù),這一部分會在本系列的后續(xù)文章詳述。

四、總結(jié)

??閉包就是函數(shù)能夠記住并訪問它的詞法作用域,即使當這個函數(shù)在它的詞法作用域外執(zhí)行時。函數(shù)執(zhí)行完畢后,相應的變量對象沒有作用域鏈的引用就會被當做垃圾被回收,但是如果有閉包情況會變得不一樣,閉包的作用域鏈依然對外部函數(shù)的變量對象保持引用,因此外部函數(shù)的變量對象不會被銷毀,閉包依然能夠訪問外部函數(shù)的變量。
??在JavaScript中沒有模塊的語法(ES6之前),閉包可以用來實現(xiàn)模塊模式。在函數(shù)式編程中,柯里化十分常見,可以利用閉包來實現(xiàn)柯里化。
如需轉(zhuǎn)載,煩請注明出處:www.cnblogs.com/lidengfeng/…

轉(zhuǎn)載于:https://juejin.im/post/5c8f602ce51d4566cf262fef

總結(jié)

以上是生活随笔為你收集整理的JavaScript夯实基础系列(二):闭包的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。