Come with me now on a journey through code and data...

Quirky JavaScripts

I'm working my way through the points in this list of 33 fundamentals every JavaScript developer should know in order to make sure I understand the language thoroughly. This notebook is for working through code relating to points 4 and 5.

Here's a cool talk about everything I've written notes on below.

Here's a cool table that demonstrates type comparison for == and ===. It also shows if statement type-coercion.

Here's what Mozilla have to say about comparison operators (relational & equality as described below)

Type Coercion

JavaScript is dynamically typed and uses type coercion for comparison of variables. This affects how boolean statements are evaluated, and how equality operators work.

Truthy Falsy Values

JavaScript implicitly converts values to booleans when used in statements that expect booleans (if, for, while, do-while)

Note: switch statements don't expect booleans necessarily so no type coercion is applied.

In [1]:
function printIsTruthy(value) {
    printStatement = (value) ? `${value} is truthy` : `${value} is falsy`
    console.log(printStatement)
}

Numeric values:

In [2]:
printIsTruthy(123)
printIsTruthy(0)
printIsTruthy(-1)
printIsTruthy(NaN)
123 is truthy
0 is falsy
-1 is truthy
NaN is falsy

String values

In [3]:
printIsTruthy('abc')
printIsTruthy('')
abc is truthy
 is falsy

Null and Undefined

In [4]:
printIsTruthy(null)
printIsTruthy(undefined)
null is falsy
undefined is falsy

Collections

In [5]:
printIsTruthy({}) // object
printIsTruthy([]) // array
[object Object] is truthy
 is truthy

Logical Operators

Logical operators && || will implicitly convert operands to booleans (depending on the context) but the operand value itself is returned - not the converted boolean value.

In [6]:
var value = 0 || 'abc'
In [7]:
console.log(value)
abc

This trick is sometimes used for setting default values - the or operator is looking from left-to-right for the first true operand to return.

0 is falsy so is not returned, the non-empty string is truthy and so is returned and used to set the variable.

Mathematical Operators + String Concatenation

When creating calculations using + - / * % values are implicitly converted to numbers. (Except dates - which are converted to Strings...?!)

In [8]:
var value = true + null + 3
console.log(value)
4

In this example true has been converted to 1, null has been converted to 0, and 3 is obviously 3

Since + is used for both addition of numbers and concatenation of strings (i.e. it is overloaded) there's extra quirks to be aware of.

In [9]:
var value = '3' + 0
console.log(value)
30

Since the code is evaluated and executed from left to right - the statement will first be treated as a mathematical statement - it will convert any non-string values to integers and apply mathematical operations - but as soon as it hits a string the values will start to be converted to strings and the + operator becomes a string concatenation.

In [10]:
var value = 0 + 1 + '3' + false
console.log(value)
13false
In [11]:
var value = true + 1 + 2 + '3' + 4 + 5 + true
console.log(value)
4345true

Comparison Operators

Relational Operators

When performing comparisons > < >= <= all values are implicitly converted to numbers (including dates)

Exception: strings are compared using lexicographical string comparison (alphabetical order)

Note: Any comparison involving NaN will evaluate to false

In [12]:
var value = false < '123'
console.log(value)
true

false has been converted to 0 to be used in the < comparison, and the string has been converted to a number.

In [13]:
false + true > '0' + 2
Out[13]:
false

In this case the string concatenation seems to convert values to numerics before performing the comparison

In [14]:
'0' + 2 < false + true
Out[14]:
false

Equality Operators

In JavaScript, values are considered equal if they are:

  • identical boolean values
  • identical strings
  • ... any primitives of the same value
  • numerically equivalent (as in +0 == -0)
  • a reference to the same object / array / reference value

NaN is not equal to anything - including itself

Two distinct objects are never equal for either strict or abstract comparisons

== applies type-coercion first if the types are different, then checks for equality. It uses something called the "abstract equality comparison algorothm", a series of 22 steps to determine equality.

We know that null is converted to 0 when used with mathematical or comparison operators. So you would think it would be converted to 0 in the == equality check.

In [15]:
var value = (null == 0)
console.log(value)
false

null is only coerced as undefined when using the == operator

There are other strange outputs from using the == operator - they can be explained by looking at the abstract equality comparison algorithm" but in short it doesn't always return what's expected.

When using JavaScript it's safest to always use the === operator unless you have a specific reason for using ==

=== doesn't apply type-coercion and uses the "strict equality algorithm".

Testing out some stuff

Object equality

It was mentioned above but just to demonstrate to myself:

Checking if two objects are equal using == and ===

In [16]:
var obj1 = {'value':'blah'}
var obj2 = {'value':'blah'}
In [17]:
obj1==obj2
Out[17]:
false
In [18]:
obj1===obj2
Out[18]:
false

They're not because they reference different objects in memory.

Checking if two arrays are equal using == and ===

In [19]:
var arr1 = [1,2,3]
var arr2 = [1,2,3]
In [20]:
arr1==arr2
Out[20]:
false
In [21]:
arr1===arr2
Out[21]:
false

They're not - again because of references.

Null == Undefined

The Mozilla page on comprison operations says:

Null and Undefined Types are strictly equal to themselves and abstractly equal to each other.
In [22]:
null==undefined
Out[22]:
true
In [23]:
null===undefined
Out[23]:
false

So == can be useful in the case where we don't care whether something is null or undefined, we just want to check whether it has a defined value or not.

In [24]:
var value = null

if (value==undefined) {
    console.log('No value')
}
No value