Lists of JavaScript methods which you can use natively
github.com
github.com
All kidding aside, a lot of our lodash code ends up looking something like this:
function (xs) {
return _(xs).pluck('foo').filter().value();
}
That code clearly expects that xs is an array of objects. However, we might occasionally end up with xs being undefined, or with xs being an array but one of the elements is null, etc.Most of the time, we want our function to just swallow those errors and return an empty array in such cases. This is exactly what lodash does, but if we tried to call xs.map(...) in that case we'd get an error. Similar caveats apply for grabbing the foo attribute if one of the array elements ends up being null or something.
For this reason, I recommend continuing to use lodash almost all of the time, even when there's a native Javascript method available.
No no no no. That's a silent failure and a bug. That means our code is doing something we don't intend or understand.
If you tell a computer something it doesn't understand, it should tell you that it doesn't understand. There's no scenario where "just guess what you think I wanted to do and then do it" is a safe or reliable way for a program to run. By definition, sometimes it will work as intended and sometimes it won't. That's a bug. It's the worst kind of bug, actually: silent and undetectable until you catch the problem in the final output.
In many other cases, of course, the robust thing to do is to catch a failure early and prevent some code from executing before it can do more harm, and err on the side of the system doing nothing instead of it doing something wrong. But neither of these is a universal rule.
Ever written a large code base with isolated I/O, functional code, and typed/static analysis? Because I have, and nothing unintended happens except at the I/O level.
When something unintended does happen, it throws an exception: something genuinely exceptional has happened.
This code base has yet to throw an exception in production, and it also hasn't had a bug in production (after running for 6 months with ~1,000 active users).
Also, from the sounds of it, it doesn't seem like a distributed system. Client-side JS is by its nature a distributed system, dealing with network partitions all the time because end-user internet connections are unreliable.
If a bug causes an unexpected undefined value, it will lead your code to an undefined behavior. It might work well 999 times and wipe everything on the 1000th execution. Thankfully, Javascript is mostly limited to web browsers.
Exceptions make it so that nothing unexpected happen. This is especially useful when you do not know the whole codebase. Of course, there are many cases when you can just ignore the errors. Exceptions allow you to fine tune this.
Let's not confuse "undefined", a JS value that is basically like C's NULL, with "undefined behavior", the concept from e.g. the C language spec. Operations on the JS value "undefined" are perfectly well-defined in the C sense; you can reliably test for it and have a case to handle it. In particular, the well-defined behavior for Underscore/Lodash in response to mapping over "undefined" is to return an empty array. The programmer upthread is using the library's documented and well-defined behavior; there is nothing wrong with that.
It is just like how, in C, some functions (like time()) are well-defined if you pass them a NULL pointer, and some functions (like strlen()) are not, and result in undefined behavior. In this case, the functions in question are all well-defined if you pass them "undefined".
No, it makes it so that your program surprises the user by crashing, which is (hopefully!) pretty unexpected.
Unless, of course, you use the exceptions to provide some sane default code path that handles the problem, but that's exactly what lodash is doing for you in this example.
1. Where did anyone say we were hitting undefined behavior? We're hitting a well-defined JavaScript value whose name is "undefined".
2. Why is crashing correct? Don't answer in terms of language specs, answer in terms of desired behavior for whatever you're trying to do with the program. Software engineering is a tool for accomplishing other things. Sometimes, yes, software crashing is the right thing in service of that other goal. But not always.
Crashing in such case is used to avoid incorrect functioning of the program, such as overwriting user data or introducing security vulnerabilities. By definition, if the behavior wasn't expected, the program is in unknown state. Sure, you can gracefully handle such situations (e.g. allow website to load other scripts if the particular piece that "crashes" not important for its functioning, crash just some kind-of sub-process and restart it, or — if it's user input — end up with some predefined value regardless of the incorrect input, etc.), but the correct _default_ behavior is to crash, otherwise you'll end up with unknown state and the algorithm that you wrote will be incorrect.
I recommend anyone who wants to see how much unexpected behavior is in their JavaScript programs to install typescript@next and start adding types, compiling with tsc --strictNullChecks.
More for avoiding guard scaffolding than for undefined behavior.
That's exactly what I would expect. If only everyone always thrown an exception on any undefined, life would be so much better.
There are perfectly valid reasons why silently failing is OK.
Perhaps if you are using null to represent something explicitly in your data. But silently failing on undefined is just going to lead to an other bug somewhere else entirely and half a day of debugging.
I would much rather fail early and loudly than having to hunt down some anecdotal bug that happens every prime-th national holiday and is impossible to reproduce.
Having functions/methods return undefined is a huge time and complexity saver for UI code as the application could still be in the process of getting input from the user that then would be passed off to the back-end code once the user was done changing their minds. No point in having a dropdown throw an error because the user is still deciding what they want to appear in the dropdown.
Usually errors should be sent up the stack to the request / event loop and logged / telemetry / etc.
The question here is something different, though. It's what should be considered an error, vs what should use the null object pattern. I don't think anyone can make a categorical judgement on which is better without context. It's suggested here that the null object pattern implemented by lodash is desired; I don't think it's wrong in principle to rely on it, as part of a cohesive design - e.g. it's used in such a way that it doesn't make development or finding bugs harder than necessary.
That sort of thing should not be handled by a low level utility library, because at best it will only do the right thing 50% of the time. It should be handled explicitly on a single interface between your software and the tracking module.
I would probably wrap it and explicitly ignore things in the wrapping code if the tracking module is missing.
And yes, I would rather see an empty page so I know to temporarily disable the adblocker, than have a page (e.g. seamless) that silently fails to order your food at the very last step.
You will argue that it's better for the users. No it's not. If the site is broken, it's easy for them to understand. They can at least go somewhere else to order food. If everything looks like it's working but it isn't is very-very frustrating.
I can also catch and log errors and fix them, but hidden bugs will just stay longer and frustrate users, because they will think they did something wrong.
It depends if you're running ads on the page... /s
It's why I like python's attitude toward this. It's explicit, not implicit and therefore easier to read and maintain.
Obj-C basically has the behavior GP wants: call a method on nil (aka: null) and it returns nil/false/0. It's possible to write concise & correct code that relies on this behavior.
Swift turns it into a fatal runtime error, and uses the type system to help prevent it from happening.
I think there's room for both (probably not in the same project though!). A lot of successful projects have been written in Obj-C, and some of them rely on the behavior of nil.
However, it's harder to maintain the code. You have to reason about whether nil is possible and does the code do the right thing, or did the original author forget to consider the possibility? It's really nice when the static type system forces the programmer to be explicit.
Having used both paradigms, I honestly don't know which I'd prefer for JS - especially considering it's lack of static typing. It might depend on whether I was writing application or library code.
// not null-safe:
foo.bar.baz();
// null-safe:
foo?.bar?.baz();
If your type system also does null tracking, then you can see where you might have null values and decide to use the null-safe operators. foo >>= bar >>= baz
The nice thing about this approach is that (>>=) is not specific to the Maybe type, and can be extended to other data structures, include one that passes along error messages in the "null" type through the sad path if it encounters them. This would be in the (Either a) monad.That being said, that was my first reaction too. Null safe code is so important. How you do it (be it with Flow, Lodash, whatever), that doesn't matter, but I do find myself leaning toward libraries over native when payload size doesn't matter too much because of this.
A combination of Flow, Ramda and Sanctuary (for Maybes) if you want to be a bit more niche can give some pretty amazing results.
Why not call it 'objects' rather than 'xs'?
I think that, with a language with such tiny stdlib, there always be a need for some kind of utility belt-style library ("if only for convenience", as another commenter write, which is actually the main point of such libraries). It's just a matter of choosing the best library for your coding style and project at hand. It may be a hassle, but an alternative is an stdlib the size of Python's - and I'm not sure if it's a good direction.
[1] http://ramdajs.com/0.21.0/index.html [2] https://github.com/DrBoolean/pointfree-fantasy
We depend heavily on Ramda and I like its API better than Lodash and friends (it's more guessable, more consistent), but with the changes it keeps having I regret choosing it.
Our core product is currently on a Ramda version that's so old that there's no online documentation published for it, so we just look in the source. We ought to upgrade but I'm not convinced all code paths that use R are sufficiently covered by automated tests.
From my personal experience, I got very good results using LiveScript with its default Prelude.ls lib. LS supports nearly all FP-related constructs and syntax known from many other languages, and Prelude.ls, while smaller than Ramda, is quite complete, too. That's not for every project, though: introducing another language is a bigger risk than introducing a single library.
The worse example was when they changed clone to be shallow from deep. Most code worked (shallow clone is a common case), but not quite all of it. Good luck finding which part of your code depended on deep cloning, even with tests.
So don't think you'd be able to upgrade much more often.
I really care about convenience in programming tools, I'm the end user they should be convenient for me.
It's programming not masochism.
var _ = require("lodash/fp");Not much to gain though. Just importing lodash works fine.
There's not many good alternative (there's a lot of alternative, but none that are as comprehensive), essentially has ONE person really working on it (and threatening to stop imminently), and the code is unreadable, so hopping into it once said maintainer goes poof will be tricky.
We still depend on it (no great alternative), but it's a ticking time bomb IMO.
You might be better off bundling lodash and tree shaking everything you don't actually use away.
But that's just more waiting, and you still end up downloading the ENTIRE script (unless you get REALLY fancy with webpack in a way I don't even want to think about...)
I have a friend who has done, in production, exactly what your subconscious is afraid of. I should get him to do a write up sometime: it's both amazing and horrifying at the same time...
I've thought about it a bit before, and always ignored it because there'd be no real benefit.
But i could see writing a crazy webpack config that breaks out all my lodash functions into their own bundle, then tries to add a script tag to the CDN version, doing some quick processing to determine if it's in the cache, and if it's not falling back to including my own custom-built script tag (and canceling the request to the CDN version).
I'm sure it wouldn't provide any benefit to anyone, but it would be a fun code-golf exercise!
Especially for those with empty cache (need to make 1 extra DNS request, download 1 extra file, which is mostly unneeded, etc).
Only if each site is using the exact same version of Lodash which is unlikely. Lodash is tiny anyways.
You'll need something like https://github.com/lodash/babel-plugin-lodash.
Full build (~22 kB gzipped)
import merge from `lodash/merge`
merge(options, overrides)
There's also the ES6 build which you can use better with a bundling system that supports tree shaking.At some point you'll need lodash and co again - at least for convinience.
for (var key of Object.keys(imps)) { }
I figured that would work with babel-2015 and it did work on Chrome and Safari but on Firefox visitors were getting:
Symbol.Iterator undefined
You need http://babeljs.io/docs/plugins/transform-es2015-for-of/ as well.
So there is much less risk in using _.each(imps, (value, key) => and not having to discover what you don't know.
I also added Firefox to the unit tests after that.
There's a strawman proposal out there to solve this but until then I'll be sticking with Lodash: https://github.com/leebyron/ecmascript-iterator-hof
>Important: Note that the native equivalents are array methods, and will not work with objects. If this functionality is needed, then Lodash/Underscore is the better option.
[1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
[2]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
[3]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
If you're going to use any of the suggestions, check MDN or CanIUse first.
But if you consider that every decision must be put through a cost/benefit analysis, I think the appeal is that - I (or anyone in the future that will ever touch the code) must learn JS. OK. Now they must also learn framework X. That's the added cost.
Our memory is limited too, like a computer chip. When I know JS I can instantly access what I need. If you think of a Google or cheat sheet lookup like a disk access, well memory is faster than disk.
One should not be afraid to use battle tested libraries, but don't bring dependencies for no reason.
I like this analogy - not just its application to the current conversation, but because I consider myself to be understanding of a topic when I no longer have to look it up, but instead I know the topic in principle and all I lookup are minor details (maybe like an address in memory to find something on disk?)
Is that really true, though? I mean, there most probably is some theoretical limit how much data can fit in a structure of particular shape composed of particular number of neurons, but are we really hitting that limit? Are we even capable of hitting this limit after a lifetime of learning and experiencing? It may feel that way, but it could be a side-effect of some focus-switching and memory-compressing heuristics implemented by the brain.
What I mean is that there are obviously people who learned both JS and a framework, so why wouldn't anyone be able to do this? And I can guarantee that, once you learn it, both JS and a framework fit in your 'cache' perfectly. Actually, I felt no difference between recalling things before I learned a framework and after.
In other words, learning a new framework should be possible for anyone. And programmers are expected to continuously learn new things anyway, it's not "added" cost, it's just part of the job. From this point of view, I can't see any problems with introducing a library, given that it's a good library (docs, active maintainers, community, etc.)
Well you can forget the parts of JS covered by the framework. I don't really care about the native Array.map or whatever functions, because they won't always behave as I expect, while the lodash (or equivalent) will. I can't recall off the top of my head how to use XMLHttpRequest. But I don't need to and haven't needed to in a while, because every project I've worked on uses jQuery or another framework which provided better ajax methods.
Lodash I'd say is unequivocally great (particularly the FP version, which I prefer to Ramda now), but these kind of posts aren't really for those who really need the full suite of functionality. Or at least it's very useful for those starting out on a project, who can then add in Lodash/whatever if/when the need arises, rather than reflexively adding it at the start.
The array could be null/undefined, in which case the _.filter just works fine, but Array.protoype.filter does not.
I hate to riddle my code with null checks I actually do not care about.
To be honest, I'd rather check / handle nulls rather than to rely on an external library semantics. They might also change it tomorrow and upgrading would be a pain.
I really recommend "Sebastian Markbage: Minimal API Surface Area - Learning patterns instead of frameworks" http://2014.jsconf.eu/speakers/sebastian-markbage-minimal-ap...
Development is all about tradeoffs. Liberal use of Lodash can make for clean code that abstracts away subtle browser differences. For a small download size it sounds like a great tradeoff to me.
Totally agreed about size though: it's tiny. There are other reasons to prefer stdlib but size isn't one of them.
One often doesn't need lodash. In fact, the only methods one does need are Object.keys to get the keys of an object and Array.prototype.reduce to iterate over an array, 90% of the time.
But yes, difference, zip, chunk, assign... are semantic and make for cleaner code. And yes flatten, xor are non trivial to implement, and the code one doesn't write is the code one doesn't have to test if already tested.
Of course, the best solution would be for the ECMA spec to implement most of lodash methods. I would also like to see an official way to curry, the same way Function.bind allows partial application.
_.find(items, {id: 10});https://github.com/cht8687/You-Dont-Need-Lodash-Underscore#_...
Does anyone know if V8 can do any optimizations like this for native Array functions? That would be a good thing to know before downloading lodash.
1) http://filimanjaro.com/blog/2014/introducing-lazy-evaluation...
The reason Lodash is able to do this is because the collection is wrapped in an a special object that keeps track of all of the functions you call on it. Those functions don't run until you call .value() on the whole pipeline that you have setup, and then the optimization happens.
Note that this requires a separate API. One API returns your results immediately, the other (lazy) API instead returns a wrapper object after each call without yet doing the mapping/filtering/etc, ready to add more instructions to the pipeline that you must ultimately call .value() on in order to actually run.
Native functions don't have this separate lazy API that does the wrapping with this special object, and don't have/return an object with a similar .value() method.
Not to mention that you most likely won't have the same amount of testing, speed/optimization, or "familiarity" across devs with a home-grown solution.
Yeah, don't pull in lodash if you are making a small library and you only need it once, but if you avoid lodash in a codebase where you use it a non-trivial amount, it's most likely going to be worth it.
Plus with modern (at least to js land) tree shaking, and/or the way that lodash can package each function individually, you don't need ALL of lodash in order to use one or 2 functions.
If you are only using _.forEach, then you should only include _.forEach, which comes out to about 10kb before gzip compression, 5kb after (i just built it myself to test). And all but about 1.2kb of that is "core" code, so if you add additional lodash functions, most of them will increase the code size by about 2kb at the most.
That means that for an overhead of about 8kb, you can start adding extremely useful functions all throughout your codebase with very little overhead. 1.2kb to easily and quickly iterate over an object using the key and the value in each block;
_.forEach(obj, function(value, key) { // do stuff })
Pure js: Object.keys(obj).forEach(function(key) { var value = obj[key] // do stuff })
Not that much of a difference space wise, but the performance is terrible if you are iterating over a large object, and by using Object.keys you exclude some UAs.Now let's look at _.find in an object...
_.find(obj, { 'age': 1}) // returns the first object (in the object) that has a property 'age' that equals 1.
Pure JS: var objKeys = Object.keys(obj)
var retval
for(var i; i < objKeys.length; i++) {
var value = obj[objKeys[i]]
if ('age' in value && value.age === 1) {
retval = value
break
}
}
And that's untested, will only work for an object of objects, has unknown performance, and sure as shit took me more than 1 extra second to write (about 5 minutes once i looked up what the compatibility of the 'in' operator is).You could use the native .find() function, but that won't work on chrome for android, android browser, IE (any version), safari 7 and older, and opera. Plus you'd still need something like Object.values() in order to convert the "parent" object into an array to search through.
Plus what if i want to find an object in an array and in another object? (which I actually just had to do, which is why i thought of this use case) Well now you've just doubled your code, doubled the amount of tests you need to write, doubled the chance for random bugs to crop up (what happens if one of the object values is undefined? What happens if obj is null?).
With stuff like the lodash cli where you can make builds that literally only contain what you need, the lodash babel plugin that will allow you to require lodash functions and it will build only what you need, or webpack which will tree-shake away everything you don't need (and actually does a better job than the lodash-cli does!), there's no need to avoid lodash like the plague.
I'm not saying include all of it, i'm not saying use it in everything, i'm saying that you should use your brain and include it if it's going to increase the speed that you can develop, reduce the number of bugs you will face, simplify your codebase, and won't negatively impact your users. (after all, a single image will blow the 5kb of js you'll be saving out of the water)
I think developers should be expected to understand the native constructs of the language they use. You previously also talked about "being more familiar to onboarding developers". Why should Lodash be more familiar than the native looping constructs to anyone? Why would Lodash be more familiar to a developer you're onboarding who is more used to some other library completely?
If you're not used to using Lodash, then it would not be a time savings to use it, as you'd have to spend time learning its idioms. It means you've probably learned other idioms, instead (perhaps, you know, the ones you should have learned to be able to claim to know how to use the language). So if you know the native idioms, there isn't a point to learning Lodash.
You had to take time to look up operator-in because you're not used to using operator-in. Regardless, it was a waste of time. What's wrong with the following code?
for(var k in obj){
if(obj[k].age === 1){
// there you go
}
}
And I mean problems with that code itself, not problems that are actually a problem with upstream code? And how much of the problems with that upstream code are because your project is a Gordian Knot of 3rd party dependencies?You default assume other people's code is better than yours. In the last 20 years of software development, I've seen code better than mine in several standard libraries. But the JS ecosystem is not one of them.
I'm using in (`if('age' in value)`), but not for..in. There's a difference. I don't need to use that (you didn't in your example, even though you should have), but then my code would blow up in many of the same cases as yours will.
Anyway, what's wrong with that code is you'll need a hasOwnProperty check, you'll need to break out of the loop when done, it blows up if 'age' isn't a property on one of the objects in the object, it blows up if one of the properties of obj isn't an object, it blows up if obj is null/undefined, and it will deoptimize the entire function it's in in most cases in V8.
To at least fix the bugs that yours has that mine didn't, your code should be:
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
var val = obj[key]
if ('age' in val && val.age === 1) {
// there you go
}
}
}
but again that still needs some undefined/null guards at a few spots, and it needs to be able to handle both an array and an obj at where obj is to really be an equivalent of what i was using _.filter for.>You default assume other people's code is better than yours.
No, I know the lodash code i'm using is better than mine because i've looked at it, i've looked at it's test coverage, i've looked at it's performance numbers, and i've looked at it's usage.
Also... >I skipped over it because it's a solution in search of a problem.
Then why did you skip this part in my last comment:
>it's disingenuous at best and borderline lying to say that lodash is 500kb. The DEVELOPER version is 500kb, the production version is 67kb before gzipping, and 22kb after gzipping
You made me curious, so i actually took a look at my current in-development app i'm writing that i include lodash. The grand total from all lodash functions is 22k un-gzipped, and 7k gzipped.
For that i've significantly reduced my code complexity, increased compatibility, reduced the amount of tests i needed to run and maintain, and improved performance in one pretty out-there edge case.
You don't need to use lodash, and in many cases you shouldn't. But don't act like your applications are better than others in any way because you aren't using lodash.
No it doesn't. It will return as undefined, which evaluates to false when compared to 1 using either triple- or double-equals.
And why call hasOwnProperty here? I'm pretty sure I want to know the age of any properties inherited into my object from a superclass.
At worst, I will grant you an undefined/null check:
var val = null;
for(var k in obj){
if(obj[k] && obj[k].age === 1){
val = obj[k];
break;
}
}
This works with both objects and arrays.Because he's a better programmer than you are, and he knows it.
> It's just a loop.
There are many kinds of common patterns in loops, abstracting them into resuable higher order functions, aka a collections api is rather basic functional programming; "it's just a loop" is a blub phrase of someone who doesn't see the point because they have tried it long enough to understand the value in a different paradigm.
If you've been around for a while, you should have already accepted the benefits of functional programming in the places it's most effective and banished low level procedural programming of loops except in cases where extreme optimization is required. There's no eb and flow here, higher order map/reduce/filter style has been the best way since the 60/70/80's when Lisp and Smalltalk were doing it.
I guess the babel polyfill would do this.
https://github.com/cht8687/You-Dont-Need-Lodash-Underscore/b...
Cripes!
The good thing is, at some point in the future you can just drop the shim and everything keeps working.
Lodash does has a lot of additional nice utility functions though.
the real way to do this is to write ES6 and compile to ES5 and provide both to the browser.
[].concat(array).sort()But that's not your priority when building website, it doesn't matter if your JS is 60Kb or 20. No one cares how much dependencies you build system pulls. You need tested, reliable and working libs to do their job, and not write your own routines for 3 months.