I don’t know JS

So I’ve kinda got stuck in to learning JavaScript. I’ve previously said that I think JavaScript is like the assembler of the web: coding in vanilla JS isn’t really something you should do because it’s too easy to get things wrong, so you should have a layer of abstraction on top.

But…oddly…I’ve actually really got into vanilla JS.

I was thinking about the origin of this and I think it was that I got stuck reading ES6 code, or was baffled by it in the Laracasts Vue.js tutorials. So I started doing Wes Bos’s ES6 tutorials (paid but excellent – Laracasts also now has an ES6/ES2015 series) and it kinda went from there.

On suggestion from a friend, I got a couple of Kyle Simpson’s excellent You Don’t Know JS books (<- Note: affiliate link!), and have got stuck into the first one: Up and Going. Which is short (about 50 pages of real content), but...well. My mind has been blown.

JS: Behind the scenes

I’m the kinda person that likes to know what’s going on behind the scenes. And I’ve never really understood exactly how JavaScript works. And so learning it form first principles is quite revelatory. It’s actually very different to other languages that I was brought up on in quite a number of ways. So no wonder it always baffled and freaked me out a little.

Here are some of the quirks/oddities/differences that I’ve discovered along the way.

Values are typed, variables are not

Yep. This may seem obvious, but it’s actually quite a shift of thinking from someone who’s previously coded in strictly-typed languages. I’ve never even come across the phrase “values are typed” before.

What does this mean?

Well, in other languages you have typed variables. So when you declare a variable you give it a type:

int age = 0;

for example. This can be handy because the program can detect when a value that is not an integer is assigned to the variable, which can catch errors. But it’s also constraining because you can’t assign, say, a null or false value to say that there isn’t a number yet.

Does it affect how I code? Probably not. But it’s good to know and understand.

Equality shenanigans

On top of type coercion (more below) when doing equality, and issues with comparing references (also see below) there’s also this list of unusual values and their equal-nesses.  Some of these are really quite bizarre:

  • 0 === -0 //  true - what the heck is -0 anyway?
  • NaN === NaN // false, because NaN is defined to be unequal to any other value
  • and so on

What does this mean? Well, it’s just stuff you need to know.

Does it affect how I code? Not in a big way. It may help with debugging when values aren’t what you expect them to be. It’s knowledge that’s also helpful for the odd challenges at “return true“.

Objects/Arrays/etc are assigned and passed as references

Forgive me if I don’t get my terminology exactly right, but “non-primitive” values like objects and arrays are passed around as references.

What does this mean?

Well, if the same object is assigned to two variables – which is possible because assignment is of the reference and not a copied value – then updating one will update the other:

let a = {
  x: 1,
  y: 2
};
let b = a;
b.x = 4;
console.log(a);

Logs:

[object Object] {
  x: 4,
  y: 2
}

Does it affect how I code? Yes. Stuff like this is REALLY good to know.

Objects/Arrays/etc equality is not by value

The fact that non-primitives are assigned and passed by reference also means that comparisons are also made on references, not values.

What does this mean? So you have:
[1,2,3] == [1,2,3] // false - they are two different arrays
And also, if you want to copy an array you have to do it via some other means than simple assignment (the ES6 rest/spread operator helps here).

I also learned that when coerced to a string, arrays are joined with a comma!:
[1,2,3] == "1,2,3" // true!
Does this affect how I code?

Yes. Be careful with non-primitives!

Object wrappers – e.g. `String` vs `string`

I confess I still don’t understand this – need to do more reading. But:

typeof("a"); // gives "string"

but string values are actually “wrapped” by the type String. And this “wrapping” is also true for all primitive types.  So you can see this:

a = new String;

typeof(a); // gives "object"

I’m not quite sure what it means yet and I don’t think it will affect my programming. But who knows what else I’ll find out?

Inequality not the same as (symmetric) non-equality – WHAT?!?!

This line appears in one of the books and also, at first, confused me:

The ! forms [of equality operators] are of course the symmetric “not equal” versions of their counterparts; non-equality should not be confused with inequality.

It’s later on made…hmm…clearer (?) that inequality is the greater-than, less-than and so-on conditional operators.

What does this mean? Oh. Well. Nothing. It was an interesting interlude though.

Onwards. What else?

Oh. Equality?

So with the knowledge that values, not variables, are typed, some other things need to be put into place.

One of which is the equality operators. They aren’t “typed equality” and “untyped equality”. They are actually compare-without-coercion and compare-with-coercion.

What does this mean? Well, it means that understanding the coercion can help you understand how to write correct conditions.

Does this affect my code?  I think so, in subtle ways. It should make things a bit more predictable.

Hoisting applies to functions too…sort of?

I only learned about variable hoisting recently while learning about let and const in ES6.

With non-ES6 var declarations, if you make them in the middle of a scope they are “hoisted” to the top of the scope. So when the code is running, the variable exists in an undefined state in the scope until the point of the var declaration.  Best explained in an example. Here we use a variable without ever declaring it (you’ll need to put this in a file and run it, not just type it into the console):

console.log(x);

> ReferenceError: x is not defined

Then if we use the variable before later declaring it:

console.log(x);

var x = 4;

> undefined

Spot the difference!

What does this mean?

Well not much in reality, though it was good to understand.

What is interesting is that function declaration is also effectively variable declaration, and so functions can be used before they are declared…

But…oh…hang on.  They’re hoisted AND DEFINED!:

foo();

function foo() {
 console.log('foo');
}

> "foo"

That’s not undefined. OK. I’m curious now. So what happens if I…:

bar();

var bar = function() {
 console.log('bar');
}


> TypeError: bar is not a function

Oh. Well, that’s not entirely simple then.

Will this affect my code? Probably not, but it’s good to know how both variables and functions are hoisted, even if the behaviour is slightly different in a way that I don’t currently understand.

Initial variable assignment without var makes a global

I’d forgotten this! I used to not use var at all when declaring variables and was forever accidentally making globals.

What does this mean?  That I’ve already been on a journey of learning JavaScript long enough that I’ve forgotten about this.

Will it affect my code? Well, no. But it’s a good reminder of how far I’ve come, even before the last few months.

What do I think of JS now?

Well so far so geeky.  Let’s wrap up.

This almost feels more like research than practical learning. Though I’m hopeful that learning these things will have very positive practical benefits. I’m coming to know JavaScript as a fascinating and mis-understood teenager of a language.

I still don’t like it. I still wouldn’t want anyone to learn it as a first language. In fact, in some ways, understanding it more reinforces this view.

JavaScript is a language of enigma and subtlety. Things are not always what they seem. And choosing to know it deeply is not, actually, something for the beginner coder (or the faint-hearted).

I hope you’ve enjoyed a little tour of some of the subtleties. I hope that you have learned something new. And I hope that you are encouraged to learn more!