日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

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

我眼中的 JavaScript 函數式編程

瀏覽:87日期:2023-11-14 14:11:46

JavaScript 函數式編程是一個存在了很久的話題,但似乎從 2016 年開始,它變得越來越火熱。這可能是因為 ES6 語法對于函數式編程更為友好,也可能是因為諸如 RxJS (ReactiveX) 等函數式框架的流行。

我眼中的 JavaScript 函數式編程

看過許多關于函數式編程的講解,但是其中大部分是停留在理論層面,還有一些是僅針對 Haskell 等純函數式編程語言的。而本文旨在聊一聊我眼中的函數式編程在 JavaScript 中的具體實踐,之所以是 “我眼中的” 即我所說的僅代表個人觀點,可能和部分 嚴格概念 是有沖突的。

本文將略去一大堆形式化的概念介紹,重點展示在 JavaScript 中到底什么是函數式的代碼、函數式代碼與一般寫法有什么區別、函數式的代碼能給我們帶來什么好處以及常見的一些函數式模型都有哪些。

 我理解的函數式編程

我認為函數式編程可以理解為,以函數作為主要載體的編程方式,用函數去拆解、抽象一般的表達式

與命令式相比,這樣做的好處在哪?主要有以下幾點:

語義更加清晰可復用性更高可維護性更好作用域局限,副作用少基本的函數式編程

下面例子是一個具體的函數式體現

// 數組中每個單詞,首字母大寫// 一般寫法const arr = ['apple', 'pen', 'apple-pen'];for(const i in arr){ const c = arr[i][0]; arr[i] = c.toUpperCase() + arr[i].slice(1);}console.log(arr);// 函數式寫法一function upperFirst(word) { return word[0].toUpperCase() + word.slice(1);}function wordToUpperCase(arr) { return arr.map(upperFirst);}console.log(wordToUpperCase(['apple', 'pen', 'apple-pen']));// 函數式寫法二console.log(arr.map(['apple', 'pen', 'apple-pen'], word => word[0].toUpperCase() + word.slice(1)));

當情況變得更加復雜時,表達式的寫法會遇到幾個問題:

表意不明顯,逐漸變得難以維護復用性差,會產生更多的代碼量會產生很多中間變量

函數式編程很好的解決了上述問題。首先參看 函數式寫法一,它利用了函數封裝性將功能做拆解(粒度不唯一),并封裝為不同的函數,而再利用組合的調用達到目的。這樣做使得表意清晰,易于維護、復用以及擴展。其次利用 高階函數,Array.map 代替 for…of 做數組遍歷,減少了中間變量和操作。

函數式寫法一函數式寫法二 之間的主要差別在于,可以考慮函數是否后續有復用的可能,如果沒有,則后者更優。

鏈式優化

從上面 函數式寫法二 中我們可以看出,函數式代碼在寫的過程中,很容易造成 橫向延展,即產生多層嵌套,下面我們舉個比較極端點的例子。

// 計算數字之和// 一般寫法console.log(1 + 2 + 3 - 4)// 函數式寫法function sum(a, b) { return a + b;}function sub(a, b) { return a - b;}console.log(sub(sum(sum(1, 2), 3), 4);

本例僅為展示 橫向延展 的比較極端的情況,隨著函數的嵌套層數不斷增多,導致代碼的可讀性大幅下降,還很容易產生錯誤。

在這種情況下,我們可以考慮多種優化方式,比如下面的 鏈式優化

// 優化寫法 (嗯,你沒看錯,這就是 lodash 的鏈式寫法)const utils = { chain(a) { this._temp = a; return this; }, sum(b) { this._temp += b; return this; }, sub(b) { this._temp -= b; return this; }, value() { const _temp = this._temp; this._temp = undefined; return _temp; }};console.log(utils.chain(1).sum(2).sum(3).sub(4).value());

這樣改寫后,結構會整體變得比較清晰,而且鏈的每一環在做什么也可以很容易的展現出來。函數的嵌套和鏈式的對比還有一個很好的例子,那就是 回調函數Promise 模式

// 順序請求兩個接口// 回調函數import $ from 'jquery';$.post('a/url/to/target', (rs) => { if(rs){ $.post('a/url/to/another/target', (rs2) => { if(rs2){$.post('a/url/to/third/target'); } }); }});// Promiseimport request from 'catta'; // catta 是一個輕量級請求工具,支持 fetch,jsonp,ajax,無依賴request('a/url/to/target') .then(rs => rs ? $.post('a/url/to/another/target') : Promise.reject()) .then(rs2 => rs2 ? $.post('a/url/to/third/target') : Promise.reject());

隨著回調函數嵌套層級和單層復雜度增加,它將會變得臃腫且難以維護,而 Promise 的鏈式結構,在高復雜度時,仍能縱向擴展,而且層次隔離很清晰。

 常見的函數式編程模型閉包(Closure)

可以保留局部變量不被釋放的代碼塊,被稱為一個閉包

閉包的概念比較抽象,相信大家都或多或少知道、用到這個特性

那么閉包到底能給我們帶來什么好處?

先來看一下如何創建一個閉包:

// 創建一個閉包function makeCounter() { let k = 0; return function() { return ++k; };}const counter = makeCounter();console.log(counter()); // 1console.log(counter()); // 2

makeCounter 這個函數的代碼塊,在返回的函數中,對局部變量 k ,進行了引用,導致局部變量無法在函數執行結束后,被系統回收掉,從而產生了閉包。而這個閉包的作用就是,“保留住“ 了局部變量,使內層函數調用時,可以重復使用該變量;而不同于全局變量,該變量只能在函數內部被引用。

換句話說,閉包其實就是創造出了一些函數私有的 ”持久化變量“。

所以從這個例子,我們可以總結出,閉包的創造條件是:

存在內、外兩層函數內層函數對外層函數的局部變量進行了引用

閉包的用途

閉包的主要用途就是可以定義一些作用域局限的持久化變量,這些變量可以用來做緩存或者計算的中間量等等。

// 簡單的緩存工具// 匿名函數創造了一個閉包const cache = (function() { const store = {}; return { get(key) { return store[key]; }, set(key, val) { store[key] = val; } }}());cache.set('a', 1);cache.get('a'); // 1

上面例子是一個簡單的緩存工具的實現,匿名函數創造了一個閉包,使得 store 對象 ,一直可以被引用,不會被回收。

閉包的弊端

持久化變量不會被正常釋放,持續占用內存空間,很容易造成內存浪費,所以一般需要一些額外手動的清理機制。

高階函數

接受或者返回一個函數的函數稱為高階函數

聽上去很高冷的一個詞匯,但是其實我們經常用到,只是原來不知道他們的名字而已。JavaScript 語言是原生支持高階函數的,因為 JavaScript 的函數是一等公民,它既可以作為參數又可以作為另一個函數的返回值使用。

我們經常可以在 JavaScript 中見到許多原生的高階函數,例如 Array.map , Array.reduce , Array.filter

下面以 map 為例,我們看看他是如何使用的

map (映射)

映射是對集合而言的,即把集合的每一項都做相同的變換,產生一個新的集合

map 作為一個高階函數,他接受一個函數參數作為映射的邏輯

// 數組中每一項加一,組成一個新數組// 一般寫法const arr = [1,2,3];const rs = [];for(const n of arr){ rs.push(++n);}console.log(rs)// map改寫const arr = [1,2,3];const rs = arr.map(n => ++n);

上面一般寫法,利用 for...of 循環的方式遍歷數組會產生額外的操作,而且有改變原數組的風險

而 map 函數封裝了必要的操作,使我們僅需要關心映射邏輯的函數實現即可,減少了代碼量,也降低了副作用產生的風險。

柯里化(Currying)

給定一個函數的部分參數,生成一個接受其他參數的新函數

可能不常聽到這個名詞,但是用過 undescore 或 lodash 的人都見過他。

有一個神奇的 _.partial 函數,它就是柯里化的實現

// 獲取目標文件對基礎路徑的相對路徑// 一般寫法const BASE = '/path/to/base';const relativePath = path.relative(BASE, '/some/path');// _.parical 改寫const BASE = '/path/to/base';const relativeFromBase = _.partial(path.relative, BASE);const relativePath = relativeFromBase('/some/path');

通過 _.partial ,我們得到了新的函數 relativeFromBase ,這個函數在調用時就相當于調用 path.relative ,并默認將第一個參數傳入 BASE ,后續傳入的參數順序后置。

本例中,我們真正想完成的操作是每次獲得相對于 BASE 的路徑,而非相對于任何路徑。柯里化可以使我們只關心函數的部分參數,使函數的用途更加清晰,調用更加簡單。

組合(Composing)

將多個函數的能力合并,創造一個新的函數

同樣你第一次見到他可能還是在 lodash 中,compose 方法(現在叫 flow)

// 數組中每個單詞大寫,做 Base64// 一般寫法 (其中一種)const arr = ['pen', 'apple', 'applypen'];const rs = [];for(const w of arr){ rs.push(btoa(w.toUpperCase()));}console.log(rs);// _.flow 改寫const arr = ['pen', 'apple', 'applypen'];const upperAndBase64 = _.partialRight(_.map, _.flow(_.upperCase, btoa));console.log(upperAndBase64(arr));

_.flow 將轉大寫和轉 Base64 的函數的能力合并,生成一個新的函數。方便作為參數函數或后續復用。

 自己的觀點

我理解的 JavaScript 函數式編程,可能和許多傳統概念不同。我并不只認為 高階函數 算函數式編程,其他的諸如普通函數結合調用、鏈式結構等,我都認為屬于函數式編程的范疇,只要他們是以函數作為主要載體的。

而我認為函數式編程并不是必須的,它也不應該是一個強制的規定或要求。與面向對象或其他思想一樣,它也是其中一種方式。我們更多情況下,應該是幾者的結合,而不是局限于概念。

標簽: JavaScript
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
婷婷精品久久久久久久久久不卡| 一区免费视频| 欧美一区久久久| 日韩在线成人| 欧美在线资源| 久久精品国产在热久久| 婷婷精品在线| 免费美女久久99| 国产午夜精品一区在线观看| 日韩三级精品| 久久亚洲一区| 亚洲经典在线| 蜜桃一区二区三区| 国产毛片一区二区三区| 在线综合视频| 国产高清一区| 亚洲视频综合| 色天使综合视频| 欧美精品高清| 精品成人免费一区二区在线播放| 麻豆久久一区| 欧美日韩亚洲一区二区三区在线| 亚洲青青久久| 亚州国产精品| 亚洲欧美日韩国产一区| 在线亚洲免费| 91成人精品视频| 免费精品国产的网站免费观看| 久久香蕉网站| 国产成人精品一区二区三区在线| 久久影视三级福利片| 欧美激情亚洲| 日本在线高清| 亚洲精品一区三区三区在线观看| 国产亚洲网站| 蜜桃av一区二区在线观看| 亚洲欧美日韩在线观看a三区| 亚洲精品九九| 日韩欧美在线精品| 国产精品白丝av嫩草影院| 国产一区二区久久久久| 国产精品国产三级国产在线观看| 欧美色图一区| 日韩视频一区| 免费在线观看日韩欧美| 97成人超碰| 老牛国内精品亚洲成av人片| 麻豆久久久久久| 久久久蜜桃一区二区人| 蜜桃tv一区二区三区| 日韩精品导航| 日本国产欧美| 精品视频亚洲| 国产模特精品视频久久久久| 婷婷综合电影| 国产高清亚洲| 国产亚洲一区二区手机在线观看 | 综合亚洲视频| 黄色网一区二区| 日韩av免费| 视频一区在线播放| 欧美激情五月| 中文字幕在线免费观看视频| 99视频在线精品国自产拍免费观看| 日本不卡高清| 久久男人av| 免播放器亚洲| 国产欧美日韩精品高清二区综合区| 国产成人1区| 亚洲欧洲日韩| 久久精品福利| 亚洲一区导航| 91日韩在线| 亚洲一区二区小说| 中文一区一区三区高中清不卡免费| 在线亚洲一区| 国产精品国产一区| 99精品99| 麻豆精品在线| 蜜桃视频在线观看一区二区| 卡一卡二国产精品| 欧美精品一线| 精品视频久久| 亚洲在线国产日韩欧美| 久久久久久自在自线| 免费看日韩精品| 国产欧美日韩在线观看视频| 99综合视频| 国产精品毛片视频| 蜜桃视频免费观看一区| 国产成人调教视频在线观看| 蜜臀精品一区二区三区在线观看| 久久香蕉网站| 久久国产精品毛片| 国产精品欧美三级在线观看 | 蜜桃av.网站在线观看| 中文字幕日韩亚洲| 中文字幕在线官网| 日本中文字幕一区二区视频| 欧美特黄一级大片| av资源新版天堂在线| 国产精品一区二区精品| 蜜臀av国产精品久久久久| 国产aⅴ精品一区二区三区久久| 国产一区清纯| 粉嫩av一区二区三区四区五区 | 久久亚洲黄色| 亚洲资源在线| 欧美日韩国产高清| 精品国产网站| 天堂精品久久久久| 亚洲手机在线| sm捆绑调教国产免费网站在线观看 | 丝袜诱惑制服诱惑色一区在线观看 | 亚洲免费一区二区| 亚洲综合在线电影| 亚洲国产成人二区| 国产精品一区亚洲| 久久久成人网| 日本综合字幕| 国产精品丝袜在线播放| 欧美日本不卡高清| 爽爽淫人综合网网站| 亚洲www啪成人一区二区| 成人在线免费观看网站| 亚洲+小说+欧美+激情+另类| 综合激情一区| 99国产精品私拍| 亚洲成人精品| 一区二区精品伦理...| 国产精品久久| 久久久久亚洲精品中文字幕| 国产精品三级| 午夜电影一区| 玖玖玖国产精品| 1024精品久久久久久久久| 麻豆成全视频免费观看在线看| 国产精品一国产精品| 亚洲精品欧美| 日韩和欧美一区二区三区| 99国产精品视频免费观看一公开| 午夜国产欧美理论在线播放| 欧美韩日一区| 精品国产a一区二区三区v免费| 国产91在线精品| 免费在线欧美黄色| 国产一区二区三区不卡视频网站 | 午夜性色一区二区三区免费视频| 国产亚洲网站| 99综合视频| 亚洲人成亚洲精品| 女同性一区二区三区人了人一| 国产农村妇女精品一二区| 日韩精品影视| 免费不卡中文字幕在线| 日韩专区欧美专区| 蜜臀va亚洲va欧美va天堂| 日本91福利区| 国产乱人伦丫前精品视频| 日韩1区2区日韩1区2区| 久久精品资源| 精品国产麻豆| 91成人精品视频| 红桃视频国产精品| 免费在线观看不卡| 麻豆精品一区二区综合av| 国产欧美啪啪| 精品三级在线观看视频| 精品国产aⅴ| 精品欧美一区二区三区在线观看| 亚洲中午字幕| 亚州国产精品| 狠狠久久伊人中文字幕| 日韩欧美不卡| 亚洲性视频h| 天堂va在线高清一区| 久久国产视频网| 国产精品18| 中文字幕成在线观看| 久久网站免费观看| 日韩av一二三| 国产精品久久久久久久久久久久久久久 | 韩国久久久久久| 老鸭窝亚洲一区二区三区| 在线亚洲免费| 国产欧美一区二区三区精品观看| 精品少妇av| 婷婷丁香综合| 免费人成精品欧美精品| 精品国产乱码久久久久久樱花| 国产h片在线观看| 国产高清久久| 国产免费久久| 波多野结衣久久精品| 亚洲精品乱码久久久久久蜜桃麻豆| 国产精品网在线观看| 亚洲一级少妇| 91免费精品国偷自产在线在线| 国产精品黄色| 精品成人免费一区二区在线播放|