本篇文章為大家帶來了關於javascript的相關知識,其中主要介紹了函數式程式設計的相關問題,函數式程式設計可以理解為,以函數作為主要載體的程式設計方式,用函數去拆解、抽像一般的表達式,希望對大家有幫助。
相關推薦:javascript學習教學
#看過許多關於函數式程式設計的講解,但是其中大部分是停留在理論層面,還有一些是僅針對Haskell 等純函數式程式語言的。而本文旨在聊一聊我眼中的函數式程式設計在 JavaScript 中的具體實踐,之所以是 「我眼中的」 即我所說的僅代表個人觀點,可能和部分 嚴格概念 是有衝突的。
本文將略去一大堆形式化的概念介紹,聚焦在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 做數組遍歷,減少了中間變數和運算。
而 函數式寫法一 和 函數式寫法二 之間的主要差異在於,可以考慮函數是否後續有復用的可能,如果沒有,則後者更優。
鍊式最佳化
從上面函數式寫法二我們可以看出,函數式程式碼在寫的過程中,很容易造成橫向延展,也就是產生多層嵌套,以下我們舉個比較極端點的例子。
Javascript程式碼
// 计算数字之和 // 一般写法 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 的链式写法) Javascript代码 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 模式。
Javascript程式碼
// 顺序请求两个接口 // 回调函数 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'); } }); } }); // Promise import 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)
#可以保留局部變數不被釋放的程式碼區塊,被稱為一個閉包
閉包的概念比較抽象,相信大家都或多或少知道、用到這個特性
那麼閉包到底能帶給我們什麼好處?
先來看看如何建立一個閉包:
Javascript程式碼
// 创建一个闭包 function makeCounter() { let k = 0; return function() { return ++k; }; } const counter = makeCounter(); console.log(counter()); // 1 console.log(counter()); // 2
makeCounter 這個函數的程式碼區塊,在傳回的函數中,對局部變數k ,進行了引用,導致局部變數無法在函數執行結束後,被系統回收掉,產生了閉包。而這個閉包的作用就是,「保留住「 了局部變量,使內層函數呼叫時,可以重複使用該變數;而不同於全域變量,該變數只能在函數內部被引用。
換句話說,閉包其實就是創造出了一些函數私有的 」持久化變數「。
所以從這個例子,我們可以總結出,閉包的創造條件是:
存在內、外兩層函數
內層函數對外層函數的局部變數進行了引用
閉包的用途
閉包的主要用途就是可以定義一些作用域限制的持久化變量,這些變數可以用來做快取或計算的中間量等等。
Javascript程式碼
// 简单的缓存工具 // 匿名函数创造了一个闭包 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 作为一个高阶函数,他接受一个函数参数作为映射的逻辑
Javascript代码
// 数组中每一项加一,组成一个新数组 // 一般写法 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 函数,它就是柯里化的实现
Javascript代码
// 获取目标文件对基础路径的相对路径 // 一般写法 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)
Javascript代码
// 数组中每个单词大写,做 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教程
以上是一起聊聊JavaScript函數式編程的詳細內容。更多資訊請關注PHP中文網其他相關文章!

是的,JavaScript的引擎核心是用C語言編寫的。 1)C語言提供了高效性能和底層控制,適合JavaScript引擎的開發。 2)以V8引擎為例,其核心用C 編寫,結合了C的效率和麵向對象特性。 3)JavaScript引擎的工作原理包括解析、編譯和執行,C語言在這些過程中發揮關鍵作用。

JavaScript是現代網站的核心,因為它增強了網頁的交互性和動態性。 1)它允許在不刷新頁面的情況下改變內容,2)通過DOMAPI操作網頁,3)支持複雜的交互效果如動畫和拖放,4)優化性能和最佳實踐提高用戶體驗。

C 和JavaScript通過WebAssembly實現互操作性。 1)C 代碼編譯成WebAssembly模塊,引入到JavaScript環境中,增強計算能力。 2)在遊戲開發中,C 處理物理引擎和圖形渲染,JavaScript負責遊戲邏輯和用戶界面。

JavaScript在網站、移動應用、桌面應用和服務器端編程中均有廣泛應用。 1)在網站開發中,JavaScript與HTML、CSS一起操作DOM,實現動態效果,並支持如jQuery、React等框架。 2)通過ReactNative和Ionic,JavaScript用於開發跨平台移動應用。 3)Electron框架使JavaScript能構建桌面應用。 4)Node.js讓JavaScript在服務器端運行,支持高並發請求。

Python更適合數據科學和自動化,JavaScript更適合前端和全棧開發。 1.Python在數據科學和機器學習中表現出色,使用NumPy、Pandas等庫進行數據處理和建模。 2.Python在自動化和腳本編寫方面簡潔高效。 3.JavaScript在前端開發中不可或缺,用於構建動態網頁和單頁面應用。 4.JavaScript通過Node.js在後端開發中發揮作用,支持全棧開發。

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。1)C 用于解析JavaScript源码并生成抽象语法树。2)C 负责生成和执行字节码。3)C 实现JIT编译器,在运行时优化和编译热点代码,显著提高JavaScript的执行效率。

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

SublimeText3 Linux新版
SublimeText3 Linux最新版

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

Safe Exam Browser
Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。