我接触编程比较晚,从自学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,即输入固定参数,便可获得相同输入的函数)。优势是:
既然本文介绍的主要是函数式编程,因此主观评价了函数式的优势。固然面向对象的编程模式优势更加突出,各位客官已经很是熟悉封装、继承、多态给咱们带来的优势,代码可读性与可维护性在全部模式中名列前茅,面向对象编程位列神坛已久,在此没必要多言。