编程的本质万变不离其宗就是,操纵一坨数据。
当然操纵的方式有许多,存储的方式也五花八门,但是本质不变,就是访问数据(读取以及改变)。
词条:操作数据
面向对象:实例、原型、构造函数、原型链
函数式:作用域、作用域链、参数、递归、闭包、函数调用
1、对于OO(面向对象,下同),数据存放在对象的属性(成员变量)里面,以及静态成员(全局变量)
2、对于函数式,数据存放在闭包(各级作用域)里面,作用域包括全局作用域。
数据存放方式决定了访问的方式。
1、对于OO来说,访问数据(全局变量除外)需要先获取对象的引用,然后再进行操作(直接访问——公共属性,或者调用成员函数/方法访问——私有属性)
2、对于函数式,访问数据是直接访问(通过函数入参或者作用域链查找)
// OO
class Foo {
constructor(){
this.bar = 0
}
}
let foo = new Foo()
foo.bar ++
// 函数式
let bar = 0
function foo(){
bar ++
}
foo()
从中我们可以看出一些东西,OO是通过持有,以及传递对象的方式去让别的对象来操作数据,而对象也会是其他对象的成员,层层嵌套。当你想要访问某一个数据的时候,就需要顺着对象的引用链条去找,一步步去操作。
函数式传递的则是函数,调用函数即操作数据,传递函数的时候其实隐含着传递了函数创建的时候所附带的作用域,这个在表面上看不出来,在底层是有的。
为了理解这句话,我们来看高阶函数的本质
function foo(x){
let bar = x
return function(){
return bar
}
}
let Bar = foo(1)
console.log(Bar())//1
当调用foo(1)的时候返回一个函数,这个函数可以访问foo函数内部的bar变量,这就是高阶函数。如果翻译成OO思想大家就知道怎么回事了。
class Foo{
constructor(x){
this.bar = x
}
Bar(){
return this.bar
}
}
let foo = new Foo(1)
console.log(foo.Bar())
看上去是不是差不多。其实在C#中对lambda表达式的编译就是这个原理。会内部创建一些类。
当然高阶函数的写法肯定不会这么啰嗦,会写的比较优雅。(这就是本质区别)
let foo = bar=>()=>bar
let Bar = foo(1)
console.log(Bar())//1
所以OO编程,是面向作用域编程,而函数式编程,是面向功能编程。
参考:https://zhuanlan.zhihu.com/p/57708956
https://blog.csdn.net/mrgglxy/article/details/80571290
总结:
函数式编程把数据与操作逻辑区分开, 然后各种组合;
面向对象把数据与操作逻辑合并到一块, 形成一个封装, 再各种应用;
分开使用显然比合在一起使用要灵活, 真正需要封装的时候再去谈面向对象…
偏偏人们已经把面向对象当成了组织代码的方式了, 不需要封装的时候也非要用面向对象…
函数式编程对开发者要求更高一些,需要对程序的理解更深入。面向对象则更浅显,所以为了让公司里面良莠不齐的开发人员统一协作,就逐渐采用了以面向对象为主的开发模式。
闭包和对象都是把数据从stack复制到heap里去
程序本质就是操作数据,oop和fp都把数据封装到属性中,oop通过对象封装和操作属性。fp通过函数来操作属性。