Passing additional parameters in higher-order functions

前端 未结 6 1421
刺人心
刺人心 2020-12-13 11:01

Consider this example:



        
6条回答
  •  一整个雨季
    2020-12-13 11:44

    Here's one for you:

    There are a couple of answers that talk about curry, and partial-application.

    And that's a great direction.

    But once you really get higher-order functions, you can make this stuff really clean and easy to work with.

    const curry = (f, ...initialArgs) => (...extraArgs) => {
      const args = [...initialArgs, ...extraArgs];
      return args.length >= f.length ? f(...args) : curry(f, ...args);
    };
    

    So what does that do?
    It lets you pass in a function, and gives you a function. Until you have passed in enough arguments to run the function, it's going to keep passing you another function that expects more arguments.

    What good is that?

    const multiply = curry((x, y) => x * y);
    const double = multiply(2);
    const triple = multiply(3);
    
    double(2); // 4
    triple(9); // 27
    

    Now it's really easy to define something like your test.

    const notEqual = curry((test, x) => test !== x);
    
    // you could do it like this, to reuse `notFoo`
    const notFoo = notEqual("foo");
    samples.filter(notFoo);
    
    // you could do it like this, if you don't need `notFoo`
    samples.filter(notEqual("foo"));
    

    But wait! There's more!

    const filter = curry((predicate, array) => array.filter(predicate));
    
    const removeFoos = filter(notEqual("foo"));
    removeFoos(samples);
    removeFoos(items);
    removeFoos(otherStuff);
    

    Now I have a function that filters out foos and I can just pass it arrays whenever I feel like it.

    Last one for now:

    const compose = (...fs) => x => fs.reduceRight((x, f) => f(x), x);
    

    Instead of writing

    h(g(f(x)));
    

    Compose lets me write

    const hgf = compose(h, g, f);
    hgf(x);
    hgf(y);
    hgf(z);
    
    // it's read from right to left
    const tto = compose(three, two, one);
    
    // or from bottom to top
    const tsf = compose(
      third,
      second,
      first
    );
    
    // because it runs like
    y = third(second(first(x)));
    

    So now, let's try something wild...

    // lib functions (Ramda would work fine)
    const map = curry((transform, array) => array.map(transform));
    const reduce = curry((summarize, seed, array) => 
      array.reduce(summarize, seed));
    const flatMap = curry((transform, array) =>
      array.map(transform).reduce((a, b) => a.concat(b), []));
    
    // business functions
    const castToEmployee = personData => new Employee(personData);
    const isWorking = ({ active }) => active;
    const removeSuperiors = curry((user, employee) =>
      employee.role <= user.role);
    
    const customEmployeeCriteria = (criteria, employee) => { /*...*/ };
    const removeDuplicates = (arr, employee) =>
      arr.some(person => person.id === employee.id)
        ? arr
        : arr.concat(employee);
    

    Library Code

    const performCustomSearch = searchCriteria => 
      filter(cutomEmployeeCriteria(searchCriteria));
    
    const getAuthorizedEmployeeList = currentUser =>
      filter(removeSuperiors(currentUser));
    
    const buildEmployees = compose(
      filter(isWorking),
      map(castToEmployee),
    );
    
    const cleanResults = compose(
      filter(removeBrokenItem),
      map(removePrivateMembers),
      reduce(removeDuplicates, []),
    );
    
    const handleEmployeeRequest = (currentUser, searchCriteria) => compose(
      cleanResults,
      performCustomSearch(searchCriteria),
      getAuthorizedEmployeeList(currentUser),
      buildEmployees
    );
    

    API Code

    //(maybe /employees/?search={...}&token=123)
    router.get("/employees", (req, res) => {
      PersonService.getAll()
        .then(handleEmployeeRequest(req.user, req.query.search))
        .then(filteredEmployees => res.json(filteredEmployees));
    });
    

    And we're done.
    Easy as pie.

提交回复
热议问题