简体   繁体   中英

Converting object literals into functions in ES6 Javascript

I was reading this article at hackernoon.com that tries to explain a particular approach to turn javascript Switch/Case into a functional version of it self.

The article reaches a point where they set a function called switchcase , defined as:

const switchcase = cases => defaultCase => key =>
  cases.hasOwnProperty(key) ? cases[key] : defaultCase

They also say that this function has a problem, as "the entire object literal is evaluated before being passed to the switchcase function". And so they decide to convert the values in the object literal to functions. To do so they utilize this syntax:

const switchcaseF = cases => defaultCase => key =>
  switchcase(cases)(defaultCase)(key)()

My question is: How does this last syntax work? Can anyone break it down for me?

By using functions, you need to take a function for defaultCase as well.

 const switchcase = cases => defaultCase => key => cases.hasOwnProperty(key)? cases[key]: defaultCase const switchcaseF = cases => defaultCase => key => switchcase(cases)(() => defaultCase)(key)() // ^^^^^^^^^^^^^^^^^ ^^ console.log(switchcaseF({ foo: () => 'bar' })('nothing')('foo')); console.log(switchcaseF({ foo: () => 'bar' })('nothing')('baz'));

It would be cleared if rewrite this with simple function.

First, rewrite switchcase . This function is a result of currying next function with three arguments

function switchcase(cases, defaultCase, key){
    if(cases.hasOwnProperty(key)){
        return cases[key];
    }
    return defaultCase;
}

So, if rewrite switchcaseF we get next:

function switchcaseF(cases, defaultCase, key){
    var selectedFunc = switchcase(cases, defaultCase, key);

    return selectedFunc();
}

How does this last syntax work? Can anyone break it down for me?

Consider the definition of switchcase and switchcaseF .

const switchcase = cases => defaultCase => key =>
  cases.hasOwnProperty(key) ? cases[key] : defaultCase

const switchcaseF = cases => defaultCase => key =>
  switchcase(cases)(defaultCase)(key)()

If we inline the application of switchcase within switchaseF we get the following.

const switchcaseF = cases => defaultCase => key =>
  (cases.hasOwnProperty(key) ? cases[key] : defaultCase)()
//|____________________________________________________|
//                           |
//          switchcase(cases)(defaultCase)(key)

Furthermore, we can move the function application inside the conditional expression.

const switchcaseF = cases => defaultCase => key =>
  cases.hasOwnProperty(key) ? cases[key]() : defaultCase()

Now, consider the example from the article that you linked to.

const counter = (state = 0, action) =>
  switchcaseF({
    'INCREMENT': () => state + 1,
    'DECREMENT': () => state - 1
  })(() => state)(action.type)

If we inline the application of switchcaseF within counter we get the following.

const counter = (state = 0, action) => {
  const cases = {
    'INCREMENT': () => state + 1,
    'DECREMENT': () => state - 1
  }
  const defaultCase = () => state
  const key = action.type
  return cases.hasOwnProperty(key) ? cases[key]() : defaultCase()
}

Hence, if action.type is 'INCREMENT' then the result is state + 1 . If action.type is 'DECREMENT' then the result is state - 1 . Otherwise, the result is state .

The reason we write expressions like () => state + 1 instead of simply state + 1 is for lazy evaluation . We only evaluate the body of () => state + 1 when the function is called. This prevents incorrect behaviour like in the following example.

 const switchcase = cases => defaultCase => key => cases.hasOwnProperty(key)? cases[key]: defaultCase const never = () => { while (true); } const example = key => switchcase({ never: never() })('it works')(key) console.log(example('it should work')) // expected 'it works' but never returns

Using switchcaseF solves this problem.

 const switchcaseF = cases => defaultCase => key => cases.hasOwnProperty(key)? cases[key](): defaultCase() const never = () => { while (true); } const example = key => switchcaseF({ never: () => never() })(() => 'it works')(key) console.log(example('it should work')) // 'it works' as expected

However, note that you can use getters to make it work with switchcase too.

 const switchcase = cases => defaultCase => key => cases.hasOwnProperty(key)? cases[key]: defaultCase const never = () => { while (true); } const example = key => switchcase({ get never() { return never(); } })('it works')(key) console.log(example('it should work')) // 'it works' as expected

We can also make defaultCase lazy.

 const switchcase2 = cases => defaultCase => key => cases.hasOwnProperty(key)? cases[key]: defaultCase.value const never = () => { while (true); } const example = key => switchcase2({ get never() { return never(); } })({ get value() { console.log('yes'); return 'it works'; } })(key) console.log(example('it should work')) // 'yes' 'it works' as expected

If you don't want it to be lazy then you can wrap it in strict as follows.

 const switchcase2 = cases => defaultCase => key => cases.hasOwnProperty(key)? cases[key]: defaultCase.value const never = () => { while (true); } const strict = value => ({ value }) const example = key => switchcase2({ get never() { return never(); } })(strict('it works'))(key) console.log(example('it should work')) // 'it works' as expected

Hope that elucidates your doubts.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM