我接觸編程比較晚,從自學java開始,面向對象的思想就已經深刻骨髓。以前那些年,個人代碼只有這一種編碼風格。前端
這些年來,js發生了翻天覆地的變化,前端已經遠不是那個dom橫行,ajax調用接口的時代。數據驅動、函數式(聲明式編程)、工程化、Node、狀態管理等大量新興的技術進入眼簾。咱們親眼見證前端代碼從面向對象到函數式的轉變,從抵制到接受,從學習到驚歎,驚歎一等對象的神奇,驚歎僅僅聲明配置就能夠完成功能,驚歎js竟然有這樣的高玩。java
固然,我也見過太多的人對函數式嗤之以鼻,以爲函數式編程難以維護,在業務複雜的場景下容易造成維護噩夢(asserts hell)。今天,以我我的的立場(徹底中立,不帶任何面癱色彩),就函數式編程與面向對象編程作簡單的博弈,順便介紹下從Java中spring框架就開始興起的控制反轉思想,這種思想的兩個組成部分依賴注入和依賴收集正式大名鼎鼎的angular的mvvm的實現原理。程序員
咱們有兩種編程方式:命令式和聲明式。面向對象編程屬於命令編程與聲明式的結合。
咱們能夠像下面這樣定義它們之間的不一樣:ajax
舉個簡單的例子,假設咱們想讓一個數組裏的數值翻倍。spring
命令式:編程
var numbers = [1,2,3,4,5]
var doubled = []
for(var i = 0; i < numbers.length; i++) {
var newNumber = numbers[i] * 2
doubled.push (newNumber)
}
console.log (doubled) //=> [2,4,6,8,10]複製代碼
聲明式json
var numbers = [1,2,3,4,5]
var doubled = numbers.map (function (n) {
return n * 2
})
console.log (doubled) //=> [2,4,6,8,10]複製代碼
map函數所作的事情是將直接遍歷整個數組的過程概括抽離出來,讓咱們專一於描述咱們想要的是什麼(what)。注意,咱們傳入map的是一個純函數;它不具備任何反作用(不會改變外部狀態),它只是接收一個數字,返回乘以二後的值。
在一些具備函數式編程特徵的語言裏,對於 list數據類型的操做,還有一些其餘經常使用的聲明式的函數方法。例如,求一個list裏全部值的和,命令式編程會這樣作:數組
var numbers = [1,2,3,4,5]
var total = 0 for(var i = 0; i < numbers.length; i++) {
total += numbers[i]
}
console.log (total) //=> 15複製代碼
而在聲明式編程方式裏,咱們使用reduce函數:閉包
var numbers = [1,2,3,4,5]
var total = numbers.reduce (function (sum, n) {
return sum + n
});
console.log (total) //=> 15複製代碼
reduce函數利用傳入的函數把一個list運算成一個值。它以這個函數爲參數,數組裏的每一個元素都要通過它的處理。每一次調用,第一個參數(這裏是sum)都是這個函數處理前一個值時返回的結果,而第二個參數(n)就是當前元素。這樣下來,每此處理的新元素都會合計到sum中,最終咱們獲得的是整個數組的和
。架構
一樣,reduce函數概括抽離了咱們如何遍歷數組和狀態管理部分的實現,提供給咱們一個通用的方式來把一個list合併成一個值。咱們須要作的只是指明咱們想要的是什麼?
從聲明式編程誕生的那天起,對聲明式編程與命令式編程的討論就沒有中止過。做爲程序員,咱們很是習慣去命令計算機去作某些事情。遍歷列表,判斷,賦值已是咱們邏輯中最多見的代碼。
在不少狀況中,命令式編程確實很是直觀、簡單而且編碼運行效率最高,最重要的,維護的人也很是容易理解。加上大多數人並不理解函數的本質,只能把邏輯與數據封裝到一個個對象中,以上的種種緣由,致使聲明式編程一直沒有成爲主流的編程模式。甚至有人以爲聲明式編程是反人類思惟模式的編程,只是爲了寫一些所謂高大上的「玩具」產生的模式。
若是咱們花時間去學習聲明式的能夠概括抽離的部分,它們能爲咱們的編程帶來巨大的便捷。首先,我能夠少寫代碼,這就是通往成功的捷徑。其次,咱們能夠抽象出很是實用的工具類,對對象或者函數進行深度加工,嵌套,運算,直到獲得想要的結果。最後,每當有需求變動時候,大多數狀況下,咱們無需改寫框架(聲明分析)代碼,只須要修改聲明的配置便可完成需求變動。
最重要的,它們能讓咱們站在更高的層面是思考,站在雲端思考咱們想要的是什麼,什麼是變化的,什麼是不變的,找到變化,配置之,找到不變,封裝之。最後你會發現,咱們不關心變化,由於變化的經過配置來聲明,咱們只關心不變,也就是框架,用框架(不變)來處理聲明(變化),正如道家的哲學,以不變(框架)應萬變(聲明)。而不是站在底層,思考事情該如何去作。
(一般來講,核心的架構師編寫不變的框架,低P/T編寫配置聲明,不要覺得配置僅僅是json等格式,在函數式編程裏,配置每每是函數/類或者任何對象)
將現實世界的物體抽象成類,每一個物體抽象成對象。用繼承來維護物體的關係,用封裝來描述物體的數據(屬性)與行爲(方法),經過封裝技術,消息機制能夠像搭積木的同樣快速開發出一個全新的系統。既能夠提升編程效率,又加強了代碼的可擴展/維護等靈活性,是世界上運用最普遍的編程方法(我的觀點:沒有之一)。
面嚮對象語言是命令式編程的一種抽象。抽象包括兩方面,數據抽象與過程抽象。在JS中,面向對象編程(也就是咱們常說的基於對象,由於JS並非面向對象的語言)把邏輯與數據封裝到函數與原型中,經過函數的原型鏈拷貝實現繼承,而代碼的運行邏輯與數據依然封裝在函數內,可是作了屬性與方法的區分。優秀的面向對象編程顯然能夠作到聲明式編程,也就是根據聲明配置生成結果(也就是說,面向對象編程的邏輯是預設的,咱們能夠根據輸入條件,判斷走不一樣的邏輯)。
可是絕大多數的面向對象編程,不會根據聲明配置去生成邏輯,邏輯的調用是封裝在對象中,而不是動態生成。因此並無作到真正的聲明式,也就是數據與邏輯徹底分離。這裏所說的動態生成邏輯,是根據聲明,自動完成邏輯的生成,這樣就徹底能夠不用編寫業務代碼,而僅僅靠聲明來完成邏輯的實現,而這部分處理,交給框架處理便可。
把邏輯徹底視爲函數的計算。把數據與邏輯封裝到函數中,經過對函數的計算,加工,處理,來生成新的函數,最後拼裝成一個個功能獨立的函數。在運用這些函數,完成複雜邏輯的實現。
與現象對象不一樣的是,咱們把數據和邏輯封裝到函數中而不是類與對象中。每一個函數徹底獨立,好的函數式設計,每一個函數都是一個純函數(pure function,即輸入固定參數,便可獲得相同輸入的函數)。優勢是:
既然本文介紹的主要是函數式編程,因此主觀評價了函數式的優勢。固然面向對象的編程模式優勢更加突出,各位客官已經很是熟悉封裝、繼承、多態給咱們帶來的優勢,代碼可讀性與可維護性在全部模式中名列前茅,面向對象編程位列神壇已久,在此沒必要多言。