功能组合javascript

时间:2015-12-30 20:48:07

标签: javascript functional-programming

我正在尝试理解Javascript中的功能组合,并遵循talk which describes the compose function

我尝试了以下程序,它将接受一个句子,将其分解成碎片(用空格分隔)并在每个单词上使用大写并返回一个单词数组。

function compose(f, g) {
    "use strict";
    return function() {
        return f.call(this, g.apply(this,arguments));
    }
}

var split = function (string, delim) {
    "use strict";
    return string.split(delim);
};

var uppercase = function (string) {
    "use strict";
    if (string instanceof Array) {
        return string.map(function (x) {
            return x.toString().toUpperCase();
        });
    } else {
        return string.toString().toUpperCase();
    }

    //return string.toString().toUpperCase();
};

var myfunc = compose(uppercase,split);

var data = myfunc("Elementary! My dear Watson",/\s+/);
console.log(data);

虽然我得到了我想要的东西,但代码在以下方面很难看:

  1. 我会不断重新定义split和toUpperCase “引用错误:toUpperCase未定义”。是因为他们是 方法而不是纯粹的功能本身?
  2. 大写方法很难看,因为它应该只接收一个字符串,以便翻转大小写,但是因为我正在对它进行标记,所以这个函数会接收一个数组,因此会检查丑陋的数组。
  3. 如何将代码即兴创作以具有“功能管道”即。拆分 - >地图 - >大写?

4 个答案:

答案 0 :(得分:2)

这是你所拥有的相同类型的代码,它已被简化了一点。如果这是您想要使用编码的方向,我不能推荐使用Reg Braithwait的Javascript Allonge,这完全改变了我编写代码的方式。

希望这个小例子展示了如何编写函数以及函数js的有用性。另一个很大的好处是,如果您使用的是不看外部范围的纯函数,则可以轻松调试问题,因为您可以遵循可靠的函数调用流。

学习功能足以理解它的基本概念花了我大约6个月并不断练习。但值得付出努力。

"use strict"; // you just need one use strict on the hholee file
// for compose you shouldn't really need to call or apply as this is always the window for a pure function
function compose(a, b) {
    return function( c ) {
        return a( b( c ) );
    }
}

// split is now a simple function that takes a delimeter and returns a function that splits the string
function split(delim) {
  return function( string ){
     return string.split(delim);
  }
};

// if you add a function mapper you can remove some of the complexity in uppercase
// and compose map with uppercase
function map( fn ){
  return function( arr ){
    return Array.prototype.map.call( arr, fn );
  }
}

// this is now a very simple single responsibility function that can be composed
function uppercase(string) {
    return string.toUpperCase();
};

var 
  splitBySpace   = split(/\s+/),
  arrToUpper     = map(uppercase),
  myfunc         = compose(arrToUpper,splitBySpace);

console.log( arrToUpper(['one', 'two']) );

console.log( myfunc("Elementary! My dear Watson") );

// if you want to split on a different character you will need to create a new split function by currying a different delimeter
var 
  splitByChar    = split(''),
  splitByBang    = split('!');

// you also don't have to compose the functions you can write them out normally but it is much harder to follow eg.
console.log( map(uppercase)(split('')("Elementary! My dear Watson") ) );
<script src="http://codepen.io/synthet1c/pen/WrQapG.js"></script>

答案 1 :(得分:0)

这是一个小例子,说明如何使用流畅的界面启动和运行,涵盖问题的第三部分。要带走的关键是你用new Obj创建一个对象,并在return this所需的方法中允许方法链接。

功能Javascript不允许链接方法,因为它没有内部状态,你使用函数js来装饰和混合可以返回值或其他函数的其他函数。

&#13;
&#13;
// MyObj is declared within a module to make the internals private
var myObj = (function() {

  // Constructor function
  function MyObj(string, delim) {
    this.input = string;
    this.delim = delim;
  }

  // create the MyObj methods on it's prototype
  // all methods have been decorated with the fluent function
  MyObj.prototype = {
    // split the input
    split: fluent(function(delim) {
      if( ! Array.isArray( this.input ) ){
         this.input = this.input.split( delim != null ? delim : this.delim);
      }
    }),
    // convert the text to uppercase
    uppercase: fluent(function() {
      if (Array.isArray(this.input)) {
        this.input = this.input.map(function(string) {
          return string.toUpperCase();
        });
      } else {
        this.input = this.input.toUpperCase();
      }
    }),
    // reverse the array
    reverse: fluent(function(){
      if( Array.isArray( this.input ) ){
        this.input = this.input.reverse();
      }
      else {
        this.input = this.input.split('').reverse().join('');  
      }
    }),
    // you will need a getter if you are using a fluent interface or decide what your end points will be
    get: function() {
      return this.input;
    }
  }

  return function constructor(string, delim) {
    return new MyObj(string, delim);
  }

})();

// myObj is the function to create a MyObj
console.log( myObj );

console.log( myObj("Elementary! My dear Watson", /\s+/ ).split().uppercase().get() );

// with the code you can override the default delimeter
console.log( myObj("Elementary! My dear Watson", /\s+/ ).split('').uppercase().get() );

// run different methods
console.log( myObj("Elementary! My dear Watson", /\s+/ ).split().uppercase().reverse().get() );

// order no longer matters
console.log( myObj("Elementary! My dear Watson", /\s+/ ).reverse().uppercase().split().get() );

// MyObj also has an internal state so you can do

// functional decorator - this will make the prototype methods return a value or this
function fluent(method) {
  return function() {
    var ret = method.apply(this, arguments);
    return ret != null 
      ? ret 
      : this;
  }
}
&#13;
<script src="http://codepen.io/synthet1c/pen/WrQapG.js"></script>
&#13;
&#13;
&#13;

答案 2 :(得分:0)

功能组合就是您使用两个或多个功能,并将其中一个功能制成一个功能。我写了一个非常容易理解的explanation of composing functions,我认为它将为您解决很多问题。

但是,您想要的是非常不同的东西,并且在javascript中非常简单

"Elementary! My dear Watson".split(/\s+/).map(val => val.toUpperCase());

或者如果您真的想要像上面一样的功能...

function myFunct(str, delim) {
    str.split(delim).map(val => val.toUpperCase());
}

答案 3 :(得分:0)

合成功能允许功能的可重用性。您可以应用javascript reduce方法将一组功能应用于数据。

让我们从一个基本的reduce样本开始。以下是计算销售价格的函数集。

    public static void TakeInAFunc<T>(T aFuncOrAction)
    {
        if (typeof(T) == typeof(Func<>))
        {
            // some value returned.
        }
        else if (typeof(T) == typeof(Action<>))
        {
            // it returns void.
        }
    }

我可以如下创建一个构图

const SALES_TAX = 0.08;
const COUPON_CODES = {
  AAAA: 0.1,
  BBBB: 0.07,
  CCCC: 0.15,
};

const shoppingtotal = shoppingCart =>
  shoppingCart.reduce((acc, item) => {
    acc += item.price * item.qty;
    return acc;
  }, 0);
const discount = couponCode => amount =>
  amount * (1 - COUPON_CODES[couponCode]);
const tax = amt => amt * (1 + SALES_TAX);

现在可以计算价格了,我可以叫calculatePayment。

const compose = fns => input => fns.reduce((acc, fn) => fn(acc), input);
const calculatePayment = compose([shoppingtotal, discount('AAAA'), tax]);

这是一个示例。这样,您可以轻松地组合多个函数并创建新的函数定义。