English 中文(简体)
Underscore.js: how to chain custom functions
原标题:

Using Underscore.js, I can write the following which returns 42:

_([42, 43]).chain()
    .first()
    .value()

I have custom function, not part of Underscore.js called double():

function double(value) { return value * 2; };

I would like to be able to call this function in an Underscore chain, as if it was part of Underscore. I would like to write the following, which I d like to return 84:

_([42, 43]).chain()
    .first()
    .double()
    .value()

This can t work since Underscore doesn t define double(). I could use tap() as in:

_([42, 43]).chain()
    .first()
    .tap(double)
    .value()

This is valid, but tap applies the function to its argument and returns the argument, not the result of the function. So it looks to me like I would need a sort of tap that returns the result of the function applied to its argument. Is there anything like this in Underscore.js? Am I missing something terribly obvious?

最佳回答

Not finding a tap that returns the value returns by the function is runs, I define one which I can take and add to _:

_.mixin({take: function(obj, interceptor) {
    return interceptor(obj);
}});

Then assuming I have:

function double(value) { return value * 2; };

I can write:

_([42, 43]).chain()
    .first()             // 42
    .take(double)        // Applies double to 42
    .value()             // 84

You can look at take as map on objects, instead of lists. Want to experiment with this? See this example on jsFiddle.

问题回答

So you have a custom function:

function double(value) { return value * 2; }

You can use mixin to extend Underscore with it:

_.mixin({ double:double });

Now you can call your function from the Underscore object _:

_.double(42); // 84

and from the wrapped object returned from chain:

_([42, 43]).chain()
  .first()
  .double() // double made it onto the wrapped object too
  .value(); // 84

Alright, I m fresh off of reading the underscore annotated source code for the first time. But I think you can do something like this:

function double(value) { return value * 2; };

var obj = _([42, 43]).addToWrapper({double:double});

obj.chain()
  .first()
  .double()
  .value();

The syntax/details might not be right, but the core point is this: when you call _([42,43]), you re calling underscore as a function. When you do so, it instantiates a new object and then mixes into that object most of the underscore functions. Then, it returns that object to you. You can then add your own functions to that object, and none of this pollutes the "_" namespace itself.

That s what the underscore.js code looked like to me. If I m wrong, I d like to find out and hopefully someone will explain why.

EDIT: I ve actually been using underscore.js heavily for about a month now, and I have gotten pretty familiar with it. I now know it behaves like I said here. When you call _ as a Constructor function, you get back your own "namespace" (just an object), and you can add things to it with addToWrapper() that show up in your namespace but not in the "global" "_" namespace. So the feature the OP wanted is already built in. (And I have been really impressed with underscore, btw, it is very very nicely done).

Looks like lodash has implemented exactly what you are looking for:

_.thru(value, interceptor)

from the docs:

This method is like _.tap except that it returns the result of interceptor

https://lodash.com/docs#thru

Many ways to easily achieve this, here is one such solution:

  _.chain([42,43])
    .first(1)
    .map(double)
    .first()
    .value();
    // 84

However, I would recommend using Ramda then with auto-curry and pipe / compose these kinds of tasks are trivial:

R.pipe(R.head, R.multiply(2))([42, 43]);  // 84

equivalent to:

R.compose(R.multiply(2), R.head)([42, 43]);  // 84

If you wanted to extend the solution to take say the first 2 items, instead of a single value, as requested by the OP, then:

R.pipe(R.take(2), R.map(R.multiply(2)))([42, 43])  // [84, 86]

However, in Ramda R.compose is preferred. Either way, for trivial tasks like this, it does tend to be more convenient and easy to use.

Does map work for this?

_([42, 43]).chain()
    .first()
    .map(double)
    .value()

edit

from the documentation, it looks like map would only work if you place it before the call to first:

_([42, 43]).chain()
    .map(double)
    .first()
    .value()

Using compose is another way dealing with the situation, but I think adding a function such as take as I suggested earlier is a better solution. Still, here is how the code would look like with compose:

function double(value) { return value * 2; };

_.compose(
    double,
    _.first,
    _.bind(_.identity, _, [42, 43])
)();

The initial value needs to be provided through a function which returns that value (here done by currying identity), and the functions need to be listed in an other which is the reverse of what you have with a chain, which appears as pretty unnatural to me.





相关问题
selected text in iframe

How to get a selected text inside a iframe. I my page i m having a iframe which is editable true. So how can i get the selected text in that iframe.

How to fire event handlers on the link using javascript

I would like to click a link in my page using javascript. I would like to Fire event handlers on the link without navigating. How can this be done? This has to work both in firefox and Internet ...

How to Add script codes before the </body> tag ASP.NET

Heres the problem, In Masterpage, the google analytics code were pasted before the end of body tag. In ASPX page, I need to generate a script (google addItem tracker) using codebehind ClientScript ...

Clipboard access using Javascript - sans Flash?

Is there a reliable way to access the client machine s clipboard using Javascript? I continue to run into permissions issues when attempting to do this. How does Google Docs do this? Do they use ...

javascript debugging question

I have a large javascript which I didn t write but I need to use it and I m slowely going trough it trying to figure out what does it do and how, I m using alert to print out what it does but now I ...

Parsing date like twitter

I ve made a little forum and I want parse the date on newest posts like twitter, you know "posted 40 minutes ago ","posted 1 hour ago"... What s the best way ? Thanx.

热门标签