Check out a free preview of the full The Good Parts of JavaScript and the Web course

The "Composition" Lesson is part of the full, The Good Parts of JavaScript and the Web course featured in this preview video. Here's what you'd learn in this lesson:

Where applicable, you should use elements of good composition. For example, Doug believes spaces should be used to disambiguate parentheses. Doug also shares a few examples where poor composition can lead to unintended consequences. These examples include immediately invocable functions, transitivity, and multiline string literals.


Transcript from the "Composition" Lesson

>> [MUSIC]

>> Douglas Crockford: So, we should use elements of good composition wherever applicable and I think we should, for example, use a space after a comma, not before a comma. That's how we do it in literary style. In a programming style, we should do the same thing unless we have clear evidence of a benefit that occurs from that difference.

Because we already have all this education locked into our brains about those sorts of patterns. We can use that to make it easier for writing our programs.
>> Douglas Crockford: Now programming requires significantly more precision than writing does, so we'll need additional rules. For example I propose that we use spaces to disambiguate parents.

We use parents to indicate parts of statements, to indicate invocations and function definitions and grouping. And so to help us to distinguish those cases, I propose a rule. There is no space between a function name and a left paren. And there's always one space between all other names, and a left paren.

So, by that rule these forms are all wrong. For example, return is not a function in this language. So, we shouldn't make it look like a function invocation. Now you can argue, well someone could easily figure out what these mean, right? So it doesn't matter. But it does matter because you want the person who's reading your program to understand what the program does.

You don't don't want them to be mentally correcting you and proofreading it, because that is distracting them from the much more important work of figuring out what your program does and how do we make it better. So one of the good parts in JavaScript is the immediately invocable function expression.

And we'll look a lot at these much more later today. And unfortunately the design of JavaScript didn't anticipate that this was a feature of the language. So here we've got a function statement and we want to invoke that function immediately but we can't, it's a syntax error. Someone figured out that we can wrap the function in parens.

That function is now an expression position and now we can immediately invoke it. But then you've got the extra invoking parents hanging off on the end like a pair of dog balls. So we can do better for the reader by wrapping the entire invocation expression in parens. So now the goal is not to just get it passed to the compiler, but to give a little bit more information to the reader, so that when the reader sees a function and the closing parens wrapped in parentheses, that means something.

That means what's important here is not the function, it's the result of the function. And just by moving the pair in one space over, we give a little bit more information to the reader and that can be helpful. And again we do it at no cost. So you remember earlier I warned you about automatic semicolon insertion how it sometimes puts them in places where you think it shouldn't and sometimes doesn't put them in places where it should.

This is one of those times. So here we've got an expression statement, someone left out the semi-colon expecting that the system was going to add it but in this case the system is not going to add it. So instead of assigning y to x, we're going to assign the result of calling y as a function passing the result of the other function as its argument, which is not how anybody would read this, but that's how the compiler reads it.

And so I recommend never rely on automatic semicolon insertion. Always put the semicolons in, and in the right place. If you're not sure where that is, will be really happy to tell you where they go. JavaScript has a with statement, which was modeled after the Pascal with statement.

Which was intended to make it easier to write certain kinds of function access or object accesses. Unfortunately it wasn't designed correctly. So here we've got a with statement and here we have four statements that it expands into. Can anyone guess which of these that will do?
>> Douglas Crockford: Anybody?

>> Speaker 2: Maybe it's the first one.
>> Douglas Crockford: Actually it's a trick question. It could be any of them. There's no way you can tell by reading the program, which of these it's going to do. And in fact, it's possible that every time that statement executes, it could be a different one.

>> Speaker 2: [INAUDIBLE] O.
>> Douglas Crockford: Yeah, it depends on O. And since it can't know what O is at time, there's no way to read the program. So we want to be confident that our programs are perfect, but how can you know that your program is perfect if you can't even read the program and know what it does?

So for that reason, I recommend don't ever use the with statement. You don't need it. Leave it out. You're better off without it. Now there are a lot of very clever uses for the with statement. And the people who discover these clever uses think that they should be entitled to use it, because it's so useful.

But I'm not saying it isn't useful, I'm saying there is never a case where it isn't confusing. And confusion is the enemy. Confusion must be avoided. Confusion is when a program appears to do one thing and it actually does something else. Another word for that, bug. We don't want bugs.

So I want to eliminate as many sources of confusion as possible because I'm trying to attain perfection. I may never get there but God Almighty that's where I'm going, I wanna try and make programs that are perfect. JavaScript has a double equal operator. I have problems with that, I think it should've been called equal.

But that's another thing that Thompson did to us. But unfortunately even worse than what Thomson did, it does type coercion. So first it looks at the two types of its arguments, and if they're not the same type it will convert one or both of them to another type and then compare that result.

And as a result, you get false positives and it also means you lose transitivity. Transitivity means that if two things are equal to the same thing, then they should be equal to each other. That's not the case here. So when JavaScript was developed, this was thought to be a good idea.

Turns out it's not. So the inventor of the language realized he'd made a mistake. So when it went to be standardized, he went to the standardization committee and said, I screwed up the double equal but I know the fix for it. We just have it not do type coercion.

I've already tested it in the Netscape codebase and it actually runs a little faster and it's all smaller, so that's good. It's a big win. So let's do this, right. And the committee said no, no, we're gonna keep it the way it is. But we'll offer you triple equal as a compromise.

So, a lot of people don't like triple equal because instead of having to go [NOISE]. You now have to go [NOISE] and say, yeah triple equal. Always use triple equal.
>> Speaker 2: There's JSLint.
>> Douglas Crockford: Yeah, JSLint tell you for sure. JSLint will recommend this triple equal.
>> Speaker 2: Will fix it?

>> Douglas Crockford: It won't fix it, you have to fix your own code. [LAUGH] But JSLint is very happy to tell you about the terrible things you've done but it's on you to make it right. As it should be. Now there are some cases. Because that double equal accidentally does exactly what you want.

Should you use it then? I recommend no. Don't even use it then. Because you don't want the person who's reading your code to have to ask, did he find the one case where double equal actually does exactly what you want? Or is he just incompetent? [LAUGH] You don't want people asking that question when they're reading your code, right?

You want them to understand what it's doing and go, yeah this is well written stuff, I can make this better. So if there is a feature of the language that is sometimes problematic, and if it could be replaced by another feature that is more reliable, then always use the more reliable feature.

This is a surprisingly controversial statement but I think it's obviously true. So this is a feature that was added in the s5, multiline string literals. There are lots of other languages that do this in exactly the same way. I think it was a mistake. First because it breaks indentation because the continuation has to be over on the margin.

And indentation is really important for understanding the composition of our programs right, because we've got nested functions nested objects, nested blocks. That nesting is critically important and this breaks indentation. And it does impair readability and that's a bad thing. But worse than that, there's this syntax hazard where this statement is correct.

This one is a syntax error. Can anybody spot the error in the second statement?
>> Speaker 3: There's a space at the beginning of the second line?
>> Douglas Crockford: Nope.
>> Speaker 2: Space Towards the \.
>> Douglas Crockford: Yeah, exactly, there's a space right here.
>> Speaker 2: Can't you see it?
>> Douglas Crockford: Yeah, it's obvious once you point it out, right.

But I'm trying to make programs that are perfect. I wanna be a look at and go, yes, that is perfect, and I can't. So I don't wanna use this. We spend a lot of our time searching for the needle in the haystack. We can cut that time down if our programs look less like haystacks.

So, avoid forms that are difficult to distinguish from common errors.
>> Douglas Crockford: This is another one due to Thompson. He allowed you to put assignment expressions in condition position of an if statement. Java got this right, Java says you have to put something that evaluates to a boolean. But then JavaScript got it wrong and went back to the C way and said you can put any expression in there.

So this statement appears to do what this one does. But it actually does what this one does. And so you don't ever wanna write this one. You wanna figure out which one you mean and write that instead. Make your programs look like what they do and don't make your reader guess, did you get it right or not.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now