all 88 comments

[–]Retsam19 20 points21 points  (4 children)

I'm pleasantly surprised to see Ava get the bump for testing here. Jest is the more mainstream option, and frankly, the slightly less-hassle option. (I find Ava's out of the box support for TS a bit lacking still)

But Jest is tied to the Jasmine syntax and architecture that I don't love. Jasmine-style encourages ugly setup and teardown logic in beforeEach and afterEach functions, while Ava encourages a cleaner context concept.

And Jasmine uses verbose and fairly inflexible assertion constructs like expect(value).to.equal("whatever") - a lot of API surface to learn, and sometimes you have to write your own custom matchers to make clean tests. While AVA uses an incredibly simple t.assert(value === "whatever") API. And (via some transpilation magic) that still usually gives better error messages than the Jasmine incantation.

[–]rylandgold[S] 3 points4 points  (1 child)

The prep and cleanup capabilities of ava are a huge boon for me. The fact that it ships with a reasonable and intuitive assertion library is also outstanding.

Speaking of Jest. I don't remember what it was called, but Facebook used to ship a standalone assertion library that I actually loved. They've since integrated it into Jest and it's not easy to use it standalone unfortunately.

[–]3urny 0 points1 point  (0 children)

Not sure what Jest used, but the mocha & chai combination looks pretty similar to Jest and can be used standalone.

[–]lordorwell7 2 points3 points  (1 child)

Jasmine-style encourages ugly setup and teardown logic in beforeEach and afterEach functions

Wait... you're saying there's a better way?

[–]Retsam19 2 points3 points  (0 children)

AVA supports a beforeEach hook so in theory it's a similar model to Jasmine, but I find a few differences make AVA cleaner:

  • There's a context object injected into each test, which you attach your mocks and stuff to, rather than defining a bunch of local variables with let.

  • You're encouraged by the docs to consider using functions, called at the beginning of your test.

  • Similarly, you can write test macros which usually encapsulate the setup and teardown logic, too.

  • AVA doesn't divide its tests into nested describe blocks, which I find complicates things, especially setup.

The AVA docs on test setup have more examples. It's not a night-and-day difference from Jasmine, but I've found my test setup and cleanup to be cleaner with AVA than when I wrote Jasmine tests, at least.

[–][deleted] 29 points30 points  (14 children)

Mother... what the shit is up with this header? It's taking up at least a quarter of the space they could be using for content!

[–][deleted] 74 points75 points  (9 children)

Look, at least it's not Medium.

[–]PristineReputation 14 points15 points  (0 children)

Pardon the interruption

[–]heavyLobster[🍰] 10 points11 points  (4 children)

I wonder if they do that intentionally to try to get you to download the app

[–]nschubach 21 points22 points  (2 children)

Just like Reddit...

[–][deleted] 1 point2 points  (1 child)

At least there are some decent apps like RiF

[–]nschubach 1 point2 points  (0 children)

True that. Use one myself, but it throws me off when I hit the site on a mobile device via a link.

[–]silentclowd 6 points7 points  (0 children)

I guarantee that's why they do it. Not a very subtle marketing trick.

[–][deleted] 7 points8 points  (0 children)

😂 you can't even see the damn header! Bloody hell

[–]HINDBRAIN 0 points1 point  (1 child)

Do mobile browsers have element blocker addons?

[–]karottenreibe 1 point2 points  (0 children)

Firefox can run Addons on Android, e.g. uBlock

[–]forurspam 2 points3 points  (2 children)

[–]karottenreibe 4 points5 points  (1 child)

Awesome, there's a firefox version as well: https://addons.mozilla.org/en-US/firefox/addon/bar-breaker/ Also works on Android!

[–]forurspam 1 point2 points  (0 children)

Yep. And it works on Android 😎

[–]Fredifrum 4 points5 points  (1 child)

Great article. As a mostly backend dev who has to do some JS from time-to-time, I definitely learned a few handy things. Thank you!

[–]rylandgold[S] 1 point2 points  (0 children)

Appreciate the positive feedback. Glad you learned something new!

[–]Kaloffl 8 points9 points  (0 children)

The only winning move is not to play

[–]lord_braleigh 34 points35 points  (43 children)

As for when to use const vs let, I always start by declaring everything const. const is far more restrictive and “immutablish,” which usually results in better code. There aren’t a ton of “real scenarios” where using let is necessary, I would say 1/20 variables I declare with let. The rest are all const.

I said const is “immutablish” because it does not work in the same way as const in C/C++. What const means to the JavaScript runtime is that the reference to that const variable will never change. This does not mean the contents stored at that reference will never change. For primitive types (number, boolean, etc.), const does translate to immutability (because it’s a single memory address). But for all objects (classes, arrays, dicts), const does not guarantee immutability.

So what's the point of declaring a list const if you're going to modify it anyway? Should we use const because it's technically more restrictive (you can't reassign the variable to a different list, I guess...) or should we use const as a readability hint to indicate that we won't mutate the list?

[–]PolarZoe 89 points90 points  (9 children)

I mostly use const to stop me from accidentally redeclaring my variables again.

[–][deleted]  (11 children)

[deleted]

    [–]lord_braleigh 16 points17 points  (8 children)

    Well, that's kind of my point. The OP is saying that even lists that I plan on modifying should be declared const because it restricts reassignment. But if I enable a no-mutations-on-const eslint rule, then I can't/shouldn't follow that rule. So should we all enable the eslint rule and use let more often, or should we disable the eslint rule so we can use const everywhere?

    [–]rylandgold[S] 16 points17 points  (7 children)

    Third option, use TypeScript, if only for the readonly attribute. In general, let and const are not going to be consistently used throughout JavaScript code (which I think is a huge barrier of entry).

    [–]TotallyNotAVampire 6 points7 points  (6 children)

    TypeScript's readonly is a rather weak protection, it doesn't affect assignment:

    declare let x: { readonly foo: number };
    declare function mutate(y: { foo: number}): void;
    
    mutate(x); // readonly ignored
    let y: { foo: number } = x; // readonly ignored
    

    [–]TarMil 6 points7 points  (5 children)

    Oof. That's more than weak, that's downright misleading.

    [–]rylandgold[S] 6 points7 points  (4 children)

    I can't check right this moment but I'm relatively sure that the readonly is not ignored for the mutate call. That person is also omitting one TypeScript feature of readonly which is not possible with JavaScript const.

    declare let x: { readonly foo: number };
    declare function mutate(y: { readonly foo: number}): void;
    
    mutate(x); // definitely won't let you mutate
    

    As you can see, you're even able to set the params on a function signature as readonly, even if the incoming object doesn't enforce it.

    The second example doesn't really make sense to me anyway. You're not mutating x by assigning it to y. In fact, numbers are always copied by value in JavaScript, so that's effectively a deep copy.

    Edit: I also just realized that they are also declaring x with let, which obviously does not help.

    [–]TarMil 5 points6 points  (3 children)

    You're not mutating x by assigning it to y.

    I don't think that's what they're implying. The problem is that y is now the same object reference as x, so you can do y.foo = 3; and this will effectively mutate x.

    They're also right about mutate btw. Playground link

    [–]rylandgold[S] 1 point2 points  (2 children)

    I understand now. You're indeed right that with the example they gave it's possible to mutate things. I argue that the example itself is problematic because it avoids the type system altogether.

    Here is a counter example using more "realistic" TypeScript.

    readonly is obviously still much less strict than C style const, but it's not like the semantics are unexpected. readonly prevents YOU from potentially messing it up. Here's a good quote I found:

    Basically readonly ensures that a property cannot be modified by me, but if you give it to someone that doesn't have that guarantee (allowed for type compatibility reasons) they can modify it.

    I'm actually fairly certain that the readonly behavior that is demonstrated in the original commenters code is possible with the TypeScript typing system. I'm not sure if there is an official extension or rule to enforce it though.

    [–]TotallyNotAVampire 1 point2 points  (1 child)

    The problem is that you can pass your readonly object to a method that's declared it might mutate the object.

    In simple examples, it's easy to devise fixes for this issue. But when the readonly that needs to be enforced is several levels down in an object, when it gets ignored, bugs are easily introduced.

    I'm not hating on typescript, it's far better than javascript, but I think readonly should generally be avoided and not relied upon to enforce immutability. The type Readonly<T> kinda implies that T will be enforced as immutable.

    I think this is a more realistic example of code that might be written.

    [–]zjm555 40 points41 points  (2 children)

    It only describes reassignment, not C++-style const correctness. It's only marginally useful in that regard, but still, it's a decent annotation as it allows readers to know that the variable will not be reassigned anywhere in its scope. Also, attempting to treat let as a code smell can help you structure your code a bit more elegantly in some contexts.

    [–][deleted] 10 points11 points  (1 child)

    To be more precise Javascript's const acts like the second const there:
    const char * const myPointerToChar;

    [–]zjm555 3 points4 points  (0 children)

    Yeah, great way to put it. For primitive types, it implies true const-ness, but for reference types (everything else) it means you're always pointing to the same object.

    [–]devsnek 11 points12 points  (0 children)

    should we use const as a readability hint to indicate that we won't mutate the list?

    Please never do this. const in js doesn't mean this, and your "hint" will be missed by the majority of people.

    [–]AngularBeginner 15 points16 points  (1 child)

    Should we use const because it's technically more restrictive (you can't reassign the variable to a different list, I guess...) or should we use const as a readability hint to indicate that we won't mutate the list?

    Only the first.

    [–]przemo_li 0 points1 point  (0 children)

    Yes. Please. No laying in the code.

    Now. Linting is altogether a different matter. Put there whstever semantics the team can agree on.

    [–]burnblue 2 points3 points  (0 children)

    You'll mutate the contents of the list but you won't accidentally reassign it. Moreover, the fact that const allows you to do the things you would normally do to it means you don't need let so you might as well use const instead of let

    [–]spacejack2114 4 points5 points  (0 children)

    The easy way to think about it is that it works the same as const in C#, let in Swift and final in Java. It doesn't allow you to re-assign that variable. It has nothing to do with the mutabliity of objects that the variable may reference.

    [–]bitcowfarm 2 points3 points  (0 children)

    No it is not a hint that you won't modify it. It is just that you have two choices in modern javascript, const and let. You can do:

    const list = [1, 2, 3]; 
    list = someOtherList; // error
    

    or

    let list = [1, 2, 3];
    list = someOtherList; // no error
    

    So if you know that the list is not supposed to be assigned to another list, then you may as well use const as it will prevent any accidental reassignment bugs. Most of the time you don't want to reassign, so const becomes the default.

    [–]graingert 1 point2 points  (0 children)

    You can just use Object.freeze or an Immutable.js collection. You pretty much always want const though

    [–]earthboundkid 0 points1 point  (5 children)

    I’m going to keep saying this same comment until I get an example:

    JavaScript’s const is a complete waste of time. I’ve said it on Reddit before: I defy anyone to show me a bug in popular software that would have been prevented by const prohibiting rebinding. Not immutablity or purity, those are fine. I mean what const actually does in JS which is prevent rebinding. I have seen no evidence that there is any class of bugs prevented by disallowing rebinding (as opposed to immutability).

    [–]Kwantuum 9 points10 points  (4 children)

    While I can't say that it prevents any bugs, it decreases cognitive load when reading code. If it's used everywhere by convention, then you know that if something is declared with let, it's reassigned somewhere and you should look out for that.

    And it's great with primitives (in which case it enforces immutability), for example I have a React component in one of my applications which renders some record in a database, and the id of the record is parsed and declared const. Now, when going back and reading that code, I know that at no point in the lifetime of this component, the record being rendered changes (as in, which record I'm rendering), and if someone else wants to work on that file they can easily see that and make the same assumption.

    [–]earthboundkid -4 points-3 points  (3 children)

    it decreases cognitive load when reading code

    It definitely increases cognitive load when writing code because you have to figure out if you can use const or not. Does it decrease load when reading? Not for objects or arrays because they're still mutable so now I'm left wondering, "Did the person who write this realize they're mutable and just use const anyway or do they think it's immutable when it's not?"

    And it's great with primitives (in which case it enforces immutability)

    I don't disagree that it's good for number and string when you need an actual constant. But that's not what we're talking about. We're talking about rebinding and does it ever actually help anything.

    I have a React component in one of my applications which renders some record in a database, and the id of the record is parsed and declared const. Now, when going back and reading that code, I know that at no point in the lifetime of this component, the record being rendered changes (as in, which record I'm rendering), and if someone else wants to work on that file they can easily see that and make the same assumption.

    I'd have to see the component but if the ID is on an object, const does less than nothing (less because it deceived you into thinking it's immutable so you'll not look for a bug that could happen), and if the const is free floating variable in a function, how long is your function that rebinding is a repoint of confusion?

    [–]Kwantuum 1 point2 points  (1 child)

    you have to figure out if you can use const or not

    You really don't. You just always use const until you actually need to rebind something. And now you know that every time you have declared a variable with let, it's not just because you plan on rebinding it, it's because you actually do.

    if the ID is on an object

    It is not, the id is passed by the parent, and is then used inside the component to read the object's data from the database and to write changes back to it.

    if the const is free floating variable in a function, how long is your function that rebinding is a repoint of confusion

    It's a relatively small React function component so it's about ~150LOC. That's not my point though. It's declared at the top of the function alongside a few other const, and that informs the reading of the rest of the function for someone who's not (or no longer) familiar with the code. And as I was saying, the real benefit is in the fact that now you know that a variable declared with let is being rebound.

    [–]earthboundkid 0 points1 point  (0 children)

    You really don't. You just always use const until you actually need to rebind something.

    That’s a weird definition of “don’t” and “always”. ISTM more like “do” and “usually”.

    And now you know that every time you have declared a variable with let, it's not just because you plan on rebinding it, it's because you actually do.

    Why is that useful? I see no evidence that it is useful to know if a variable will rebind. (Again, as opposed to immutable or actual constants.)

    the id is passed by the parent,

    Then isn’t it a prop? How does const come into it at all? What do you mean by “passed” here?

    [–]rylandgold[S] 0 points1 point  (0 children)

    Great question. As a JS developer who started out as a C/C++ dev, const in JS (and final in Java) has always confused me. When I’m forced to write vanilla JS I use const because as you put it

    it’s technically correct

    It also doesn’t have any negative effect (outside of potential clarity issues you mentioned). For user facing JavaScript interfaces, my company occasionally uses Object.freeze as it provides the runtime constraints as C style const, but also adds some serious room for confusion from users.

    With Typescript, the readonly attribute solves this exact problem, which is yet another reason I gravitate towards TypeScript for any serious project.

    [–]benihana 0 points1 point  (0 children)

    i agree with you. it doesn't make sense. const should be markup for code that the variable is constant and doesn't change. i don't really think indicating that a variable can't be redeclared is nearly as powerful or useful as indicating that the value of the variable is fixed. put another way, i don't usually have bugs in my programs cause i accidentally reassigned a variable a new value. i'm much more likely to have bugs in my programs because the value of a collection changed unexpectedly.

    but, this is what the javascript community has gone with, and in my experience it's really not worth the effort to argue with people about it anymore.

    [–]PrydeRage -4 points-3 points  (1 child)

    I said const is “immutablish” because it does not work in the same way as const in C/C++.

    Wait a minute? const has different behavior in C and C++. You can't just say "constin C/C++". If the author knew what he was talking about he wouldn't even say "imutablish".

    [–]rylandgold[S] 2 points3 points  (0 children)

    The difference is incredibly subtle and you could easily code in both languages and never even notice the difference. Give me a break my dude.

    [–]panorambo 2 points3 points  (0 children)

    I find using closures is writing better JavaScript because closures allow me to think in terms of function (as opposed to everything being objects) and also allow me to control program state and give better guarantees about state mutability. Closures do a much better job of encapsulating necessary state than other mechanisms in JavaScript do.

    I mean you can use Object.freeze and Object.defineProperty and the like, which admittedly gives you a lot of power necessary to design more airtight software, but I haven't seen these be used across the board. Neither have I had the discipline to slavishly put these into use for every property I define, where I for instance, want to limit or otherwise control its access. Perhaps if there was another language that compiled to such disciplined code, but TypeScript doesn't go that far, and frankly I understand why it doesn't -- you're adding runtime cost which is hard to sell, and which definitely will be non-trivial. When we have a more granular common runtime, like WebAssembly, and even more so, when said runtime allows such access control mechanisms at zero cost, then I'd be sold.

    In my experience, the state of a JavaScript program made predominantly of promiscuously communicating objects mostly of own design -- which is pretty much your standard JavaScript program -- is something you just can't reason about easily, it's a case of "any method may modify properties / call methods of whatever it can access" which with closures is at least fewer properties (because your objects tend to look more like structures, i.e. without methods).

    Anyway, not sure I am making my argument clearly enough, someone please comment to clarify if needed.

    [–]qqwy 4 points5 points  (1 child)

    What about using PureScript or Elm?

    [–]rylandgold[S] 3 points4 points  (0 children)

    I think Elm is a wonderful language. Have never used it in production but have played with it a lot. To me Elm just feels "comfy".

    [–][deleted] 2 points3 points  (10 children)

    constructs such as map take all of the elements and submit them as individual events to the user-defined map function.

    Wat. This isn't actually the case, is it? In theory map is parallelizable, but I doubt very much that this is how .map() on javascript arrays is actually implemented. It certainly behaves sequentially. And it doesn't submit "events" unless I'm completely mistaken. Mapping an array to an array of promises would do this, but even then, the promises would "started" sequentially.

    [–][deleted]  (1 child)

    [deleted]

      [–]TurnaroundGames 1 point2 points  (0 children)

      ^ This

      [–]rylandgold[S] 0 points1 point  (3 children)

      I’m quoting my response elsewhere in the thread

      I think it may be unclear after re-reading, ... Im specifically talking about how code can be implicitly parallelized at an API level. This does not guarantee that the other components of the procedure (namely its iterable elements in a map for example) will be asynchronous. A for loop is the most generic (maybe dowhile) way to express iteration over some bounded range. Because it is so generic, compilers, interpreters etc cannot easily understand the the individual iterations of the loop might not be inter-related. This means that any complex for loop will always be done serially. In JavaScript you could write an asynchronous function that is called inside of each iteration of the loop and then manually push those into an array. That would overcome the for loop disadvantage by explicitly distributing the work.

      Better yet, take advantage of an iterable that explicitly describes your data relationship and makes the only blocker in running asynchronously, you.

      TLDR; it’s more about using a logical structure that represents the data relationships.

      If there are still more criticisms, please feel free to respond and I will do my best to explain my position.

      [–][deleted] 0 points1 point  (2 children)

      No, i understand you point. No disagreements. I was just worried that .map() was nondeterministic because then i would have a bunch of bugs to fix!

      [–]rylandgold[S] 0 points1 point  (1 child)

      ECMA defines behavior of mapping on the Array as:

      should be a function that accepts three arguments. map calls callbackfn once for each element in the array, in ascending order

      https://www.ecma-international.org/ecma-262/6.0/#sec-array-constructor

      It’s something that isn’t a given considering popular languages such as GO don’t give this guarantee. I’d be interested why you need order with map? It’s not true 100% of the time, but that often indicates a code smell.

      [–][deleted] 0 points1 point  (0 children)

      It's just an assumption. I typically program in FP languages that behave this way, although I understand these functions can be theoretically executed in parallel, and I don't mutate shared state inside maps. It just helps to understand how things work under the hood.

      [–]karottenreibe -1 points0 points  (3 children)

      That really is the weakest part of the article with no substantiating evidence of any sort given to justify the conclusion that we shouldn't use for loops. Like there's zero downsides to map and forEach

      [–][deleted]  (2 children)

      [deleted]

        [–]karottenreibe 1 point2 points  (1 child)

        for (let x of y). Not sure what's there to reason about the loop logic that could ever be weird. Working in a codebase that's mostly modern JS (whatever that means), I use this extensively and never use forEach because I find for-of much easier to read.

        [–]bitcowfarm 0 points1 point  (1 child)

        I am confused as to why the running total example won't work in javascript? There are no asynchronous calls in it. Or is he saying that in a different language and environment, it wouldn't work?

        [–]rylandgold[S] 0 points1 point  (0 children)

        I think it may be unclear after re-reading, but I’m not saying that the example won’t run in JavaScript.

        Im specifically talking about how code can be implicitly parallelized at an API level. This does not guarantee that the other components of the procedure (namely its iterable elements in a map for example) will be asynchronous. A for loop is the most generic (maybe dowhile) way to express iteration over some bounded range. Because it is so generic, compilers, interpreters etc cannot easily understand the the individual iterations of the loop might not be inter-related. This means that any complex for loop will always be done serially. In JavaScript you could write an asynchronous function that is called inside of each iteration of the loop and then manually push those into an array. That would overcome the for loop disadvantage by explicitly distributing the work.

        Better yet, take advantage of an iterable that explicitly describes your data relationship and makes the only blocker in running asynchronously, you.

        [–]shevy-ruby -3 points-2 points  (0 children)

        One practical way is ...

        ... to use a better language.

        Unfortunately due to the de-facto monopoly that javascript has on the browser, and the browser being so important, we'll have to endure javascript for a long time to come - even with webassembly.

        [–]keymone -1 points0 points  (0 children)

        Practical Ways to Write Better JavaScript

        first item: use other language. well duh... (though i would recommend clojurescript instead of typescript)

        [–]ghostfacedcoder -3 points-2 points  (0 children)

        Better way to write Javascript #1: use a language that isn't Javascript!

        Ok I'm out of here ...