摘要:函數(shù)是一等公民在談到函數(shù)式編程的時(shí)候,很多時(shí)候會(huì)聽(tīng)到這樣一句話函數(shù)是一等公民。變量作用域和閉包變量作用域變量的作用域和閉包作為的基礎(chǔ),在學(xué)習(xí)函數(shù)式編程中是非常重要的,只有理解了它們,你才能更好的去理解我們后面要講到的高階函數(shù)和部分應(yīng)用等。
函數(shù)是一等公民
在談到函數(shù)式編程的時(shí)候,很多時(shí)候會(huì)聽(tīng)到這樣一句話 "函數(shù)是一等公民"。那我們?nèi)绾稳ダ斫膺@句話呢?
"一等" 這個(gè)術(shù)語(yǔ)通常用來(lái)描述值。所以當(dāng)我們說(shuō) "函數(shù)是一等公民" 時(shí),也就是說(shuō)函數(shù)擁有值的一切特性,你可以像看待一個(gè)值一樣來(lái)看待一個(gè)函數(shù)。舉個(gè)例子,數(shù)字在 JavaScript 中是一等公民,那么數(shù)字擁有的特性,也同樣被函數(shù)所擁有。
函數(shù)可以像數(shù)字一樣被存儲(chǔ)為一個(gè)變量
const num = 10; const fun = function() { return 10; }
函數(shù)可以像數(shù)字一樣作為數(shù)組的一個(gè)元素
const a = [10, function() { return 20; } ]
函數(shù)可以像數(shù)字一樣存在于對(duì)象的插槽里
const b = { name: "Tony", age: function() { return 20; } }
函數(shù)可以像數(shù)字一樣在使用時(shí)直接創(chuàng)建出來(lái)
10 + (function() { return 20; })(); // 30
函數(shù)可以像數(shù)字一樣被另一個(gè)函數(shù)返回
const well = function() { return 10; } const good = function() { return function() { return 10; }; }
函數(shù)可以像數(shù)字一樣被傳遞給另一個(gè)函數(shù)
const fun = function(value) { return value; } const happy = function(func) { return func(5) * 10; } happy(fun); // 50
最后兩條其實(shí)就是 高階函數(shù) 的定義,如果你現(xiàn)在不理解也沒(méi)有關(guān)系,我們?cè)诤竺娴牟糠謺?huì)講到它。
變量作用域和閉包 變量作用域變量的作用域和閉包作為 JavaScript 的基礎(chǔ),在學(xué)習(xí)函數(shù)式編程中是非常重要的,只有理解了它們,你才能更好的去理解我們后面要講到的高階函數(shù)和部分應(yīng)用等。
關(guān)于變量的作用域,你需要知道:
全局作用域: JavaScript 中擁有最長(zhǎng)生命周期 (一個(gè)變量的多長(zhǎng)的時(shí)間內(nèi)保持一定的值) 的變量,其變量的生命周期將跨越整個(gè)程序。
globalVariable = "This is a global variable!";
詞法作用域: 詞法作用域其實(shí)就是指一個(gè)變量的可見(jiàn)性,以及它文本表述的模擬值。
a = "outter a"; function good() { a = "middle a"; return function() { a = "inner a"; return "I am " + a; } } good(); // I am inner a
PS:這里的示例代碼僅僅是為了學(xué)習(xí),你最好不要這樣去寫,因?yàn)樗鼤?huì)讓你的代碼變得令人費(fèi)解。
在上面的例子中,我們分別對(duì) a 變量進(jìn)行了三次賦值,那么為什么最后我們拿到 a 的值是 "inner a" 而非其他呢?
當(dāng)我們聲明 a = "outter a" 時(shí),程序會(huì)在棧中開(kāi)辟一個(gè)空間去存儲(chǔ) a,當(dāng)執(zhí)行 good()函數(shù)時(shí),我們聲明了 a = "middle a",這時(shí)候會(huì)將棧中 a 的值修改掉,變成 "middle a",最后在執(zhí)行 return 語(yǔ)句時(shí),我們又聲明了 a = "inner a",這時(shí)候會(huì)再次修改棧中的 a 的值,變成 "inner a"。因此得到了上面的結(jié)果。
在多次給同一變量賦值時(shí),最后得到的值是離使用時(shí)最近的一次賦值。通過(guò)查找離使用時(shí)最近的一次賦值,我們可以快速的得出最后的結(jié)果。
動(dòng)態(tài)作用域
提到 JavaScript 的動(dòng)態(tài)作用域,就不得不提到 this 了。this 相關(guān)的知識(shí)很多,之后有時(shí)間再詳細(xì)來(lái)講講?,F(xiàn)在我們先記住 this 所指向的值由調(diào)用者確定,如下代碼所示:
function globalThis() { return this; } globalThis.call("APPLE"); //=> "APPLE" globalThis.call("ORANGE"); //=> "ORANGE"
函數(shù)作用域
閉包說(shuō)起閉包,很多人都會(huì)覺(jué)得有點(diǎn)頭疼,這的確是一個(gè)令人費(fèi)解的概念,不過(guò)不要怕,它其實(shí)沒(méi)有那么難以理解。
閉包的定義閉包是一個(gè)函數(shù)和聲明該函數(shù)的詞法環(huán)境的組合
換句話說(shuō),閉包就是在使用時(shí)被作用域封閉的變量和函數(shù)。閉包可以捕獲函數(shù)的參數(shù)和變量。
舉個(gè)例子:
const fun = function() { const a = 10; return function(b) { return a + b; } } const myFunc = fun(); // 此時(shí) myFunc 就變成一個(gè)閉包了,這個(gè)閉包可以捕獲 fun 函數(shù)里的 a 變量,b 參數(shù)。
注意閉包是在使用時(shí)才會(huì)生成的,而非創(chuàng)建時(shí)。如上面的例子,如果只創(chuàng)建 fun 函數(shù),而不執(zhí)行最后一句 fun(),那么 fun 并不能稱之為一個(gè)閉包。這里的閉包應(yīng)該是 fun 運(yùn)行時(shí)所產(chǎn)生的作用域,這個(gè)作用域捕獲了fun 里面的變量和參數(shù)。
閉包的特點(diǎn)閉包會(huì)捕獲一個(gè)值(或引用),并多次返回相同的值
每一個(gè)新的閉包都會(huì)捕獲不一樣的值
再來(lái)看一個(gè)例子:
const fun = function() { return function() { return 10; } } const myFunc = fun(); // myFunc 不是一個(gè)閉包 const fun2 = function(value) { return function() { return value; } } const myFunc2 = fun2("AWESOME"); // myFunc2 是一個(gè)閉包 myFunc2(); // AWESOME myFunc2(); // AWESOME 多次執(zhí)行 myFunc2 閉包,返回的值相同 const myFunc3 = fun2("HAPPY"); // myFunc3 是一個(gè)新的閉包 myFunc3(); // HAPPY
這里 myFunc 嚴(yán)格意義上并不能叫作一個(gè)閉包,因?yàn)樗](méi)有捕獲 fun 任何的變量或者是函數(shù)的傳參。而 myFunc2 是一個(gè)閉包,因?yàn)樗东@了 fun2 的傳參。
閉包的銷毀閉包延續(xù)了變量的生命周期,如果不手動(dòng)銷毀,閉包里面的變量會(huì)一直存在內(nèi)存中。比如當(dāng)我們手動(dòng)將 myFunc = null 時(shí),閉包里面的變量才會(huì)被垃圾回收。
實(shí)用的閉包說(shuō)了這么多,你可能會(huì)有這樣的疑問(wèn),閉包真的有用嗎?閉包一般都會(huì)用到什么地方?
*1. 用閉包模擬私有方法, 使公共函數(shù)能夠訪問(wèn)私有函數(shù)和變量,實(shí)現(xiàn)數(shù)據(jù)的隱藏和封裝。私有方法有利于限制對(duì)代碼的訪問(wèn),并且提供了強(qiáng)大的管理命名空間的能力,避免了非核心代碼對(duì)公共接口的干擾。
const Counter = () => { let count = 0; const change = (a) => { count = count + a; } return { increase: () => { change(1); }, decrease: () => { change(- 1); }, value: () => { return count; } } } const func1 = new Counter(); func1.value(); // 0 func1.increase(); func1.value(); // 1 func1.decrease(); func1.value(); // 0
*2. 通過(guò)一個(gè)高階函數(shù),生成不同的閉包,從而得到多個(gè)保存不同環(huán)境的新函數(shù)。
如下面的例子:
const makeAdder = function(x) { return function(y) { return x + y; } } const add5 = makeAdder(5); const add10 = makeAdder(10);
makeAdder 其實(shí)是一個(gè)函數(shù)工廠,用于創(chuàng)建將制定的值和它的參數(shù)求和的函數(shù)。通過(guò)它我們又創(chuàng)建了兩個(gè)新函數(shù) add5 和 add10。add5 和 add10 都是閉包。它們共享著相同的函數(shù)定義,但是卻保存了不同的環(huán)境。在 add5 的環(huán)境中,x 為 5,但是在 add10 中,x 則為10。
高階函數(shù) 定義滿足以下任意條件之一即可稱之為高階函數(shù):
以一個(gè)或者多個(gè)函數(shù)作為參數(shù)
以一個(gè)函數(shù)作為返回結(jié)果
我們常見(jiàn)的 map,find,reduce 都是以函數(shù)作為入?yún)⒌暮瘮?shù),所以它們都是高階函數(shù)。
以函數(shù)作為參數(shù)的函數(shù)使用函數(shù)作為函數(shù)的參數(shù),可以讓我們創(chuàng)建出更靈活的函數(shù)。通過(guò)將參數(shù)從值替換為函數(shù),我們可以得到更多的可能性。因?yàn)樵谡{(diào)用的時(shí)候,我們可以通過(guò)傳入不同的函數(shù)來(lái)完成不同的需求。
正如下面的例子:
const finder = function(val, func) { return val.reduce(function(prev, current) { return func(prev, current); }); } const a = [1, 2, 3, 5, 8]; finder(a, Math.max); // 8 finder(a, Math.min); // 1
在使用 finder 函數(shù)時(shí),通過(guò)傳入不同的函數(shù),最后得到了完全不同的結(jié)果。這也是為什么我們強(qiáng)調(diào) "使用函數(shù),而不是值" 的原因。
以函數(shù)作為返回結(jié)果的函數(shù)以函數(shù)作為返回結(jié)果的函數(shù),可以構(gòu)建強(qiáng)大的函數(shù)。還記得我們前面提到的閉包嗎? 通過(guò)高階函數(shù) makeAdder,我們生成了 add5 和 add10 兩個(gè)新的函數(shù)。能夠生成閉包的函數(shù),其實(shí)都是高階函數(shù)。
到這里,第一部分重點(diǎn)內(nèi)容就講完了。在下一部分中,我們會(huì)講到函數(shù)式編程中剩下的幾個(gè)重要部分:
柯里化和組合
部分應(yīng)用
遞歸
基于流的編程
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/90738.html
摘要:本文是響應(yīng)式編程第二章序列的深入研究這篇文章的學(xué)習(xí)筆記。函數(shù)科里化的基本應(yīng)用,也是函數(shù)式編程中運(yùn)算管道構(gòu)建的基本方法。四資料參考函數(shù)式編程指南 本文是Rxjs 響應(yīng)式編程-第二章:序列的深入研究這篇文章的學(xué)習(xí)筆記。示例代碼托管在:http://www.github.com/dashnowords/blogs 更多博文:《大史住在大前端》目錄 showImg(https://segme...
摘要:想學(xué)好前端,真的要主動(dòng),然后對(duì)所有的英文文檔耐心一點(diǎn)。在年月日,國(guó)際組織發(fā)布了的第六版,該版本正式名稱為,但通常被稱為或者。自此,每年發(fā)布一次新標(biāo)準(zhǔn)。但保留了用于依賴注入的構(gòu)造函數(shù)參數(shù)類型。必須在構(gòu)造函數(shù)中聲明屬性,而不是在類的代碼體中。 從 TypeScript 到 ES6 到 ES5 在我初學(xué)前端的很長(zhǎng)一段時(shí)間,不愿意碰git,不愿意碰框架,總是嫌麻煩,連ES6也沒(méi)有怎么去弄明白...
摘要:本文是響應(yīng)式編程第一章響應(yīng)式這篇文章的學(xué)習(xí)筆記。通過(guò)代碼對(duì)比可以發(fā)現(xiàn),在響應(yīng)式編程中,我們不再用對(duì)象的概念來(lái)對(duì)現(xiàn)實(shí)世界進(jìn)行建模,而是使用流的思想對(duì)信息進(jìn)行拆分和聚合。 本文是Rxjs 響應(yīng)式編程-第一章:響應(yīng)式這篇文章的學(xué)習(xí)筆記。示例代碼地址:【示例代碼】 更多文章:【《大史住在大前端》博文集目錄】 showImg(https://segmentfault.com/img/bVbuE...
摘要:本文是響應(yīng)式編程第四章構(gòu)建完整的應(yīng)用程序這篇文章的學(xué)習(xí)筆記。涉及的運(yùn)算符每隔指定時(shí)間將流中的數(shù)據(jù)以數(shù)組形式推送出去。中提供了一種叫做異步管道的模板語(yǔ)法,可以直接在的微語(yǔ)法中使用可觀測(cè)對(duì)象示例五一點(diǎn)建議一定要好好讀官方文檔。 本文是【Rxjs 響應(yīng)式編程-第四章 構(gòu)建完整的Web應(yīng)用程序】這篇文章的學(xué)習(xí)筆記。示例代碼托管在:http://www.github.com/dashnoword...
摘要:函數(shù)式編程前端掘金引言面向?qū)ο缶幊桃恢币詠?lái)都是中的主導(dǎo)范式。函數(shù)式編程是一種強(qiáng)調(diào)減少對(duì)程序外部狀態(tài)產(chǎn)生改變的方式。 JavaScript 函數(shù)式編程 - 前端 - 掘金引言 面向?qū)ο缶幊桃恢币詠?lái)都是JavaScript中的主導(dǎo)范式。JavaScript作為一門多范式編程語(yǔ)言,然而,近幾年,函數(shù)式編程越來(lái)越多得受到開(kāi)發(fā)者的青睞。函數(shù)式編程是一種強(qiáng)調(diào)減少對(duì)程序外部狀態(tài)產(chǎn)生改變的方式。因此,...
閱讀 1813·2023-04-26 02:14
閱讀 3738·2021-11-23 09:51
閱讀 1390·2021-10-13 09:39
閱讀 3981·2021-09-24 10:36
閱讀 3020·2021-09-22 15:55
閱讀 3524·2019-08-30 12:57
閱讀 2044·2019-08-29 15:30
閱讀 1988·2019-08-29 13:19