Created
February 24, 2010 05:49
-
-
Save ratbeard/313142 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
I've got some experimental stuff in unpublished branches / projects I use underscore in. Let me know if | |
you like any of these: | |
1.) Too many simple things take two lines to do in javascript, like: | |
_.reduce(list, {}, function (memo, value) { | |
memo[value] = true; | |
return memo; | |
}); | |
_.reduce(list, [], function (memo, value) { | |
memo.push(value); | |
return memo; | |
}); | |
function simpleApiWrapper(key, value) { | |
var o = {}; | |
o[key] = val; | |
apiCallThatTakesAConfigObject(o); | |
} | |
These guys can solve them, and are jqueryish: | |
_.prop = function (obj, key, value) { | |
if (value !== undefined) { | |
obj[key] = value; | |
return obj; | |
} | |
return obj[key]; | |
}; | |
_.push = function (array, value) { | |
array.push(value); | |
return array; | |
} | |
Now: | |
_.reduce(list, {}, function (memo, value) { | |
return _.prop(memo, value, true); | |
}); | |
_.reduce(list, [], function (memo, value) { | |
return _.push(memo, value); | |
}); | |
function simpleApiWrapper(key, value) { | |
apiCallThatTakesAConfigObject(_.prop({}, key, value)); | |
} | |
Also, a version of reduce that doesn't require you to return the memo is nice. You lose some functional purity, but its how reduce is used practically all the time. Thats how its implemented in jspec. Now: | |
_.managedReduce(list, {}, function (memo, value) { | |
memo[value] = true; | |
}); | |
Yes, that name sucks. | |
2.) _.extend() should accept a variable number of source objects. Like jquery, closure, and ext versions. | |
3.) let filter() and friends take flexible options: | |
_.filter(array, function (el) { return el.prop == 'hi' }); | |
_.filter(array, {prop: 'hi'}); | |
_.filter(array, 'prop', 'hi'); | |
Apply to some, every, detect, reject, etc., and map (replacing pluck). | |
I just prototyped this in a branch: http://github.com/ratbeard/underscore/tree/friendly_args | |
Needs to be implemented as some function builders, which would be useful to have on underscore as a public function (similar to _.identity). | |
4.) I've needed an _.isObject() | |
5.) I've needed a groupBy() method like: http://api.rubyonrails.org/classes/Enumerable.html#M002570 | |
6.) I've needed sum() | |
_.sum = function (obj) { | |
return _.reduce(obj, 0, function (acc, cur) { return acc + cur; }); | |
}; | |
7.) Change API of _.each to not return obj. Returning obj now for chaining seems pointless to me, unless wrapper requires it? This leaves the door open for making a fun delayed query interface, ala LINQ or datamapper. | |
_.each(students).filter('age').lt(7).all(); | |
var fn = _.each(students).filter('age').lt(7).map('name'); // doesn't call yet | |
fn.first(); // call now! | |
========================================================================== | |
I've been working more lately on more project infrastructure stuff, like: | |
- being able to turn off native versions of fn's to test our implementations, since I had a bug in one of | |
my implementations a while back that I never realized as I was just using the native version. | |
- inline documentation. I tried both pdoc and jsdoc, and both were painful. jsdoc was more mature, but: | |
- having a separate compile phase sucks | |
- its java-oriented classes, inheritence, etc. is way overkill for underscore | |
- Templates were complex | |
- making a custom tag `@native 1.6` isn't simple, and the data isn't extractable for testing purposes | |
I started to think that the best way would just be to have inline documentation as JSON objects, and | |
generate the docs page on the fly with the templating function. e.g: | |
documentation.some = { | |
desc: 'Determine if at least one element in the object matches a truth test.', | |
nativeIn: '1.6', | |
alias: 'any' | |
}; | |
_.some = function(obj, iterator, context) { | |
... | |
}; | |
Of course we would strip this out of the min versions, but this way we can use this data for | |
documentation AND testing all the permutations (native versions turned off, test all aliases for free) | |
This led me down the path of experimenting with the closure compiler, which I'd never used. I started | |
to play around with advanced optimizations when compiling with user code, to remove dead functions that | |
aren't called, which is probably the majority of them for most users. There are a few gotchas in the | |
compiler at this point | |
(http://groups.google.com/group/closure-compiler-discuss/browse_thread/thread/b59b54c1a0073aa5?pli=1), | |
but I got it working tonight where it crunches down to ~100 bytes if none of the functions are called. | |
If a user calls _.map() in their code, then that and all its dependencies are pulled in, which is | |
pretty sweet. This way we could keep _.buildLookup(), since its pretty useful but will disappear for | |
users that don't call it :) alias was stupid, I reverted that in my closure branch. | |
One change I had to make to crunch down a lot of potentially dead code is to make the user explicitly say | |
they want the OO wrapper stuff by calling `_.initWrapper()`. Otherwise, there was no other way to remove | |
that code as its referenced in the underscore constructor, and it was pulling in ~1700 bytes if I recall. | |
I make a call to `_.initWrapper` at the bottom of the file to maintain backwards compatability, which I | |
just remove in my :build_advanced rake task, so as long as a user compiles with my rake task they won't | |
get the wrapper code unless they ask for it. Or if the user comments that line out. Before 1.0, maybe you | |
want to break backwards compatibility to make advanced optimizations easier. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment