Partial Application vs. Currying(Functional Programming)
// Source: https://gist.github.com/Integralist/054e34983e8680c506c3// Source: http://www.datchley.name/currying-vs-partial-application/******************************************/// Partial Application 例子// partial is a made up functionfn=function (a, b, c) { return a + b + c }foo =partial(fn,'x','y')foo('z') // => 'xyz'// 然後 it's possible to change the arguments we partially apply:fn=function (a, b, c) { return a + b + c }foo =partial(fn,'x')foo('y','z') // => 'xyz'// Note how with Partial Application we make a function call twice (once when partially applying the arguments; and again when we fulfill the rest of the arguments).
// But remember: we can choose how many arguments we partially apply on the first call./******************************************/// Currying 例子// curry is a made up functionfn=function (a, b, c) { return a + b + c }foo =curry(fn) foo('x')('y')('z') // => 'xyz'// 然後fn=function (a, b, c) { return a + b + c }foo =curry(fn)bar =foo('x') bar('y')('z') // => 'xyz'// Note that a function that has been curried wont return the value of the function until each argument has been provided (i.e. satisfied).
// The arguments are manually partially applied one by one/******************************************/// 例子:functional programming 的實際應用場景// 1. 一個常見的 array,裡面都是物件var records = [ { id:1, name:'Dave', age:40, active:true }, { id:2, name:'Kurt', age:43, active:false }, { id:3, name:'Beth', age:28, active:true }, { id:4, name:'Angie', age:39, active:true }, { id:5, name:'Adam', age:34, active:false }];// 2. 常見的處理,用 map 取出 idvar ids =records.map(function(rec){ returnrec.id; });// 2-1. 因為是很常見的處理,所以我們可以透過 currying 讓程式可重覆使用var getProp =curry(function(prop, obj) { return obj[prop]; });var ids =records.map(getProp('id'));// 3. 更進一步,我們想讓 Array.prototype.map function 更有用,讓它可以被 partially apply a predicate function to it before giving it any data
var mapWith =curry(function(fn, list) { returnlist.map(fn); });var getNames =mapWith(getProp('name'));var names =getNames(records);// 4. 特殊技巧,composition,把它們都串起來// And if you bring in another functional programming technique called composition, along with the idea of building up smaller, reusable functions;
// you can build complex chains from those pieces with ease:var filterBy =curry(function(fn, list){ returnlist.filter(fn); });var isActive =filterBy(function(o){ returno.active ==true; });var isUnder40 =filterBy(function(o){ returno.age <40; });var getNames =mapWith(getProp('name'));functioncompose() {var args = [].slice.call(arguments), fn =args.shift(), gn =args.shift(), fog = gn ?function() { returnfn(gn.apply(this, arguments)); } : fn;returnargs.length?compose.apply(this, [fog].concat(args)) : fog;}var getActiveUnder40 =compose(getNames, isActive, isUnder40);getActiveUnder40(records);// 上行的結果會是 ['Beth', 'Angie']
/******************************************/// 下面兩個是實作時常見的手法functionfoo(a,b) {console.log("a:"+ a +", b:"+ b);}// 把 array 的每個元素當作 parameter 灑進去// 順帶一提,ES6 已經有 spread syntax 這個功能了,MDN 的介紹:https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/Spread_operator
foo.apply(null, [2,3]);// 這行的結果是,a:2, b:3// currying with bind(..)var bar =foo.bind(null,2);bar(3);// bar(3); 的結果是,a:2, b:3
上面這兩個傳 null 的作法,會讓 this 直接套用預設繫結,那麼就滿有可能直接參考到全域物件
間接參考(indirect references)也會讓 this 直接套用預設繫結
/******************************************/// 間接參考也會讓 this 直接套用預設繫結functionfoo() {console.log(this.a);}var a =2;var o = {a:3, foo: foo};var p = {a:4};o.foo();// 這行的結果是 3(p.foo =o.foo)();// 這行的結果是 2