Function Programming
本篇文章是自己对于JavaScript函数式编程一书的读书笔记。利用underscore框架介绍函数式编程几个概念。
高阶函数
1. 将函数作为参数;复制代码
function repeatedly(times,value){ return _.map(_.range(times),()=>{ return value }) } function repeatedly(times,fun){ return _.map(_.range(times),fun) } repeatedly(3,function(){ return Math.floor(Math.random()*10+1) })复制代码
repeatedly是函数式编程的一个典型思维,将值变成函数。
2. 返回其他函数的函数。复制代码
react和redux里面用了大量的返回其他函数的函数。包括高阶组件,applyMiddleware函数。 Thunk函数也是一个返回其他函数的函数。
var Thunk = function(fn){ return function(){ var args = Array.prototype.slice.call(arguments); return function(callback){ args.push(callback); return fn.apply.(this,args); } } } var readFileThunk = Thunk(fs.readFile); readFileThunk(fileA)(callback);复制代码
由函数构建函数
- 函数式的精华(dispatch函数);
function existy(x) { return x != null; } function doWhen(cond, action) { if (tructhy(cond)) return action(); else return undefined; } function invoker(NAME, METHOD) { return function (target) { if (!existy(target)) { alert('Must provide a target'); } var targetMethod = target[NAME]; var args = _.rest(arguments); return doWhen(existy(targetMethod) && METHOD === targetMethod, function () { return targetMethod.apply(target, args); }); } } function dispatch() { var funs = _.toArray(arguments); var size = funs.length; return function (target) { var ret = undefined; var args = _.rest(arguments); for (var funIndex = 0; funIndex < size; funIndex++) { var fun = funs[funIndex]; ret = fun.apply(fun,construct(target,args)) if(existy(ret)) return ret; } if(existy(ret)) return ret; } } var str = dispatch(invoker('toString',Array.prototype.toString),invoker('toString',String.prototype.toString)); console.log(str('a')); console.log(str(_.range(10)));复制代码
dispatch将多个invoker组合在一起,形成多态函数,或根据不同的参数产生不同行为的参数。 2. 柯里化。 对于每个逻辑参数,柯里化函数会逐渐返回已配置好的函数,直到所有的参数用完。
function curry(fun){ return function(firstArg){ retun function(secondArg){ return fun(firstArg,secondArg); } }}复制代码
- 部分应用。 柯里化函数逐渐返回消耗参数的函数,直到所有的参数耗尽,然而部分应用函数是一个"部分执行",等待接收剩余的参数立即执行的参数。
function partApply(f, x) { return function(y) { return f(x, y); }}复制代码
- 通过组合端至端的拼接函数。 一个理想化的函数式程序是向函数流水线的一端输送的一块数据,从另一端输出一个全新的数据块。 例如: !_isString(name);
- _isString接收一个对象,并返回一个布尔值。
- !接收一个布尔值,并返回一个布尔值。
_.compose函数从右往左执行。也就是说,最右边的函数的结果会被送入其左侧的函数,一个接一个。
function not(x){ return !x;}var isntString = _.compose(not,_isString);复制代码
纯度,不变性和更改政策。
- pure function
- 其结果只能从它的参数的值来计算。
- 不能依赖于能被外部操作改变的数据。
- 不能改变外部状态。 javaScript会导致不纯的函数或方法如Date.now(),console.log(),this和全局变量。
- 数据的不可变性。(immutablejs);
function touchAndLog(touchFn) { let data = { key: 'value' }; touchFn(data); console.log(data.key); // 猜猜会打印什么?}复制代码
在不查看 touchFn 的代码的情况下,因为不确定它对 data 做了什么,你是不可能知道会打印什么(这不是废话吗)。但如果 data 是 Immutable 的呢,你可以很肯定的知道打印的是 value。
- 更改政策。 随便拿到一个对象并改变它,更好的策略是把对象放入容器中,并更改容器。
基于流的编程。
- 惰性链。
function LazyChain(obj){ this._calls = []; this._target = obj; }LazyChain.prototype.invoke = function(methodName){ var args = _.rest(arguments); this._calls.push(function(target){ var meth = target[methodName]; return meth.apply(target,args); }) return this;}LazyChain.prototype.force = ()=>{ return _.reduce(this._calls,function(target,thunk){ return thunk(target); },this._target)}new LazyChain([1,2,3]).invoke('sort').force();复制代码