Pedro's Blog

Pedro's Blog

Javascript foundations that'll help you better understand this weird language

Javascript foundations that'll help you better understand this weird language

Understand deeper on how your development tools work, let's understand JS

Javascript can do a lot of things, some people can argue that the language doesn't make sense sometimes, but believe me it does!

Don't blame the language!! ⛔

Developers usually have a mental model of how something works when they are writing code. Usually when something doesn't work as how they pictured it, they tend to blame the technology because it doesn't make sense or because it was poorly designed.

Instead of arguing about why a technology works in a certain way, you should always try to learn why does it work like that, only then the developer will notice: Huh, it actually makes sense.

Whenever there's a divergence between what your brain thinks is happening, and what the computer does, that's where bugs enter the code. -- getify's law #17

Javascript weirdness 😕

('b' + 'a' + + 'a' + 'a').toLowerCase() // "banana" 

let x = '10'
x = x + 1 // 101
x - 1 // 100

let y = '10'
y++ //10
y  //11

let weirdBool1 = undefined == null // true
let weirdBool2 = [] == ![] // true
let weirdBool3 = 3 > 2 > 1 // false

This are some of the weird things that I have seen javascript do... If you just take a quick look at it you may think that is the language of the devil, but then again, hear me out! It all makes sense.

If you really just want to know why does that happen, then skip ahead to the last section before the conclusion. Every case gets explained. But to get a deeper understanding read the entire thing.

Let's talk about data types 📍

In javascript it's a common misconception that it's all about objects, there are objects everywhere, etc. Well not everything is an object. The javascript documentation specifies all types there are in the language, we refer to them as primitive types.

Primitive types 🦧

All primitive types that can be found in javascript are the following:

Frame 3.jpg

The Undefined type has exactly one value, called undefined. Any variable that has not been assigned a value has the value undefined.

The Null type has exactly one value, called null.

The Boolean type represents a logical entity having two values, called true and false.

The String type is generally used to represent textual data in a running ECMAScript program. It's just when you use the double quotes or single quotes.

The Symbol type is the set of all non-String values that may be used as the key of an Object property

The Number type includes all javascript's numbers, the specs divides it in number type and bigint type.

The Object type has many subtypes but it's basically a collection of properties.

Let's return to the js weirdness 🤯

typeof(undefined) // "undefined"
typeof(true) // "boolean"
typeof("I'm tired") // "string"
typeof(Symbol()) // "symbol"
typeof(5) // "number"
typeof({}) // "object"
// -----------
typeof(null) // "object"
typeof(function(){}) // "function"

Bruh, what? Didn't we just said that null is its own type? Why the typeof null is an object? typeof function(){} is a function? Dammit javascript you are not making this easy.

Since ES1 in the specs it says that if you want to unset a regular value like a number you would use undefined, but if you wanted to unset an object reference you would use null. That's why typeof null -> object.

But function is not a type! Well, tell that to javascript. In the specifications it tells us that an object that implements a call will return "function" in this case a function is a sub type of object.

Coercion, the root of all confusion 😡

In javascript we refer to type conversion as coercion. This may be one of the most important reasons why javascript seems so confusing, and it's because its behaviour.

Abstract Operations 💻

This operations are the wants you would want to use when trying to make a conversion. There's a lot of them, but here is a few of them:

  • ToPrimitive
  • ToNumber
  • ToString
  • ToBoolean

ToPrimitive is used to convert its input argument to a non-Object type. It takes an input argument and an optional argument preferredType for you to tell what would you like it to convert to, a string or a number?

ToNumber is used to convert a value into a number.

ToString is used to convert a value into a string.

ToBoolean is used to convert a value into a boolean. It introduces truthy and falsy values.

Falsy and Truthy 👀

ToBoolean operation what it actually does is to ask if a value is falsy or is it not.

Frame 4.jpg

This are all the falsy and truthy values. If its a falsy value then ToBoolean will return false. If the value that is being evaluated is any other thing then it will return true.

Coercion cases! 🗃

So what javascript does is to implicity coerce values in certain cases. Let's dive a into those examples.

Strings 🧵

let value = 10
let newString = `This is a coercion: ${value}`

let anotherCoercion = 5 + "" // "5"

Template string is probably one of the most popular implicit coercion case there's out there, in this case the value variable is being implicitly coerced into a string using the ToString abstract operation.

The plus operator first checks out the data type of both sides, if either one of both values is a string it will prefer concatanation and coerce which ever value to a string.

Numbers 🔢

anotherCoercion - 1 // 4
+'2' // 2

The minus operator will always try to coerce both values into a number if they are not a number type.

The unitary plus operator will explicitly coerce a value into a number type with the ToNumber abstract operation.

Booleans 🟢

let flag = null
while (flag) {} // also coercion

let emptyString = ""
if (emptyString) {}

In this case the flag variable is being coerced into a boolean with the ToBoolean abstractOperation. We already now that null is a falsy value, so when it gets coerced it will turn into false, same thing with the emptyString variable.

Having a clear mental model of how coercion works will help us to understand why does that early javascript weirdness works like that.

Double equals vs Triple Equals 🎏

This is where developers may have a problem with implicit coercion or just coercion in general. Some people will just prefer to use triple equals because they were thought that it prevents bugs or because it also compares typing in comparison from double equals that it doesn't.

Did you knew that javascript specs says that when using double equals, if both types are the same, it will just perform a triple equals? So actually double equals does in fact check the types of both ends of the comparison.

The thing about double equals is that it always prefers to compare numeric values, so what does it do? It coerces the values into numbers!

let weirdBool = 1 == '1' // true

That's the reason why this happens! The string value of one gets coerced into a number.

Deeper look to weird examples 🤦‍♂️

('b' + 'a' + + 'a' + 'a').toLowerCase() // "banana" 

// What it happens:
('b' + 'a' + NaN + 'a').toLowerCase()
('b' + 'a' + 'NaN' + 'a').toLowerCase()
'baNaNa'.toLowerCase() // "banana"

What is happening is that as we've already seen the unitary plus operator will explicitly coerce a value into a number with the ToNumber abstract operation.

So we are trying to coerce 'a' into a number, but it isn't a number, so it will return NaN (Not a number).

After that, because the nature of the plus operator is to always prefer concatanation, it coerces NaN into a string with the ToString abstract operator

Finally after concatanating the entire string we get 'baNaNa', the only step left is to lowercase everything an voalá!

let x = '10'
x = x + 1 // 101
x - 1 // 100

let y = '10'
y++ //10
y  //11

Until x = x + 1 everything seems reasonable, but what happens with x - 1. Well the minus operator will coerce everything that is not a number type into a number if possible.

So x gets coerced into a number and then performs the operation.

But what happens to the double plus? Just like the minus operation, the double plus will coerce any non numeric type into a number if possible, and just then will it continue with the execution.

let weirdBool1 = undefined == null // true
let weirdBool2 = [] == ![] // true
let weirdBool3 = 3 > 2 > 1 // false

We are now at the booleans, let's check them one by one.

image.png

The first variable weirdBool1 is more of a javascript specifications thing. The specs actually states that when using double equals, if you compare undefined and null it will always return true.

The second case, well... It can be translated to [] != [] Now it makes more sense! But I just want to clear something out, don't compare a value to the negation of itself, its not good code.

We have our final case, i'll just show what is happening because i'm lazy

let weirdBool3 = 3 > 2 > 1
weirdBool3 = true > 1
weirdBool3 = 1 > 1 // false

/* 
true gets coerced to one because the "<,>" 
operators coerce non numeric values to numeric.
*/

Conclusion 😀

Javascript is a weird language, definetely not perfect, but its just a matter of learning how something works to actually understand why does it behave in a certain way. Always learn how your tools work! Don't avoid this crucial step on your career.

There are more things I could talk about javascript, but I don't want to complicate too much this article. Maybe someday I will talk more about this language and its core concepts.

Reference 📄

 
Share this