# Understanding Javascript Quirks

Anupama H

@anuhosad

(Coercion, Equality & Relational Comparison, Hoisting)

• Implicit Type Coercion for operators
• ToNumber
• ToString
• ToBoolean
• ToPrimitive
• Equality Comparison
• Relational Comparison
• Hoisting

@anuhosad

## Type Coercion for Operators

@anuhosad

`[] + {} `
`"[object Object]" `
`{} + []`
`0`
`true + true`
`2`
`true / false`
`Infinity`
`false / true`
`0`
`[] * {}`
`NaN`
`{} * []`
`Uncaught SyntaxError: Unexpected token '*'`
`[] * []`
`0`
`["8"] / "2"`

@anuhosad

`["8","2"] / "2"`
`NaN`
`4`

@anuhosad

### Javascript Values

Primitive Values

Non-Primitive Values

• number

• string

• boolean

• null

• undefined
• Object

• Array

• Function

@anuhosad

Operator

Behaviour

<value1>  +  <value2>

If operands are numbers, add

If operands are strings, concatenate

All other values converted to number or string

<value1>  -  <value2>

Performs subtraction for number

Other values converted to number

<value1>  *  <value2>

Performs multiplication for number

Other values converted to number

<value1>  /  <value2>

Performs division for numbers

Other values converted to number

@anuhosad

@anuhosad

## ToNumber

Argument Result
undefined NaN
null 0
boolean true is converted to 1,
false is converted to +0
number no conversion necessary
string parse the number in the string. for example, "324" -> 324
object primValue = ToPrimitive(v, hint Number)
return ToNumber(primValue)

@anuhosad

## ToString

Argument Result
undefined "undefined"
null "null"
boolean true is "true",
false is "false"
string no conversion necessary
number the number as a string, e.g. "1.765"
object ​primValue = ToPrimitive(v, hint String)
return ToString(primValue)

@anuhosad

## ToBoolean

Argument Result
undefined false
null false
boolean no conversion necessary
string "" is false, else true
number false if +0, -0 or NaN
else true
object true

@anuhosad

```true + true
```
```true / false
```
```null + undefined
```
```null + null
```

@anuhosad

`"12" + 9 - 4 `
`= 1 + 1`
`= 2`
`= 1 / 0`
`= Infinity`
`= "129" - 4`
`= 125`
`= 0 + NaN`
`= NaN`
`= 0 + 0`
`= 0`

All these works on Primitive values

But, what if the operands are
Non-Primitive values ?

@anuhosad

## ToPrimitive

` ToPrimitive(input, PreferredType?)`

1. If input is primitive, return it as is

2. Otherwise, input is an object. Call obj.valueOf(). If the result is primitive, return it

3. Otherwise, call obj.toString(). If the result is a primitive, return it

4. Otherwise, throw a TypeError

@anuhosad

=> PreferredType is either Number or String, default is Number

=> Above is assuming PreferredType = Number, if PreferredType = String, steps 2 & 3 are swapped

=> If PreferredType is missing then is set to String for Date instances & to Number for all other values

var arr = [];

arr.valueOf() === arr

var obj = {};

obj.valueOf() === obj

var arr = [];

arr.toString() === ""

var obj = {};

obj.toString() === "[object Object]"

@anuhosad

``````var obj = {
valueOf: function () {
console.log("valueOf");
return {}; // not a primitive
},
toString: function () {
console.log("toString");
/* return the meaning of life! */
return 42;
}
};

console.log(obj * 2);  // 84

``````

## ToPrimitive (contd..)

``````var obj = { valueOf: function () { return 2 } };

console.log(6 + obj); // 8``````
``````var obj = { toString: function () { return "def" } };

console.log("abc" + obj); // abcdef``````

@anuhosad

`[] + {} `
`  [].toString() + {}.toString()`
`"" + "[object Object]"`
`"[object Object]"`
`  [].valueOf() + {}.valueOf()`

@anuhosad

```{} + []

```
`  +[]`
`  +""`
`   0`

Code Block

`+[].toString()`
`Number("")`
`+[].valueOf()`

@anuhosad

```{} + {}
```

` +{}`

Code Block

` +"[object Object]"`
` NaN`
`+{}.toString()`
`+{}.valueOf()`

@anuhosad

`({} + {}) `
`"[object Object][object Object]"`

Parenthesis prevents {} being treated as a code block

@anuhosad

`[200] + 100 `
`  [200].toString() + 100`
`"200" + 100`
`"200100"`
`  [200].valueOf() + 100`

@anuhosad

`[200] / 100 `
`  [200].toString() / 100`
`"200" / 100`
`= 2`
`  [200].valueOf() / 100`

@anuhosad

`200 / 100`
`+[100, 200] `
`  +[100, 200].toString()`
`+"100,200"`
`NaN`
`  +[100,200].valueOf()`

@anuhosad

```[] * {}
```
```{} * []
```
`[] * [] `
```[] / true
```
`(function(){return 100}) + 100`
`= "function(){return 100}100"`

@anuhosad

```= "" * "[object Object]"
```
```= NaN
```
```= *[]
```
```= Syntax Error
```
```= "" * ""
```
```= 0 * 0
```
```= 0
```
```= "" / 1
```
```= 0 / 1
```
```= 0
```

## Equality Comparison

`[] == 0`
```= true

```
```0 == null
```
```= false

```
`new String("abc") == "abc"`
```= true

```
`[1,2,3] == 123`
```= false

```
`[123] == 123`
```= true

```

@anuhosad

`new String("abc") === "abc"`
```= false

```
`    String("abc") === "abc"`
```= true

```

### Abstract Equality Comparison Algorithm

@anuhosad

1. undefined == undefined

### Abstract Equality Comparison (contd..)

@anuhosad

2. null == null

3. NaN != NaN

4. both objects -> compare references

5. null == undefined & undefined == null

6. string -> convert to number

7. boolean -> convert to number

8. object -> convert to primitive (hint: number)

`[] == 0`
`= true`
`[].valueOf() == 0`
`[].toString() == 0`
`"" == 0`
`0 == 0`

@anuhosad

`[1,2,3] == 123`
```= false

```
`ToPrimitive([1,2,3]) == 123`
`ToNumber("1,2,3") == 123`
`NaN == 123`
`"1,2,3" == 123`

@anuhosad

`[123] == 123`
```= true

```
`ToPrimitive([123]) == 123`
`ToNumber("123") == 123`
`123 == 123`
`"123" == 123`

@anuhosad

`[] == ![]`
```= true

```
`[] == false`
`0 == false`
`0 == 0`
`"" == false`

@anuhosad

```const a = [1, 2, 3]
const b = [1, 2, 3]
const c = "1,2,3"

console.log(a == c)

console.log(a == b)```
```= true
```
`new String("abc") == "abc"`
```= true

```
```0 == null
```
```= false

```

@anuhosad

`new String("abc") === "abc"`
```= false

```
```("1,2,3" == "1,2,3")
```
```= false
```
`    String("abc") === "abc"`
```= true

```

@anuhosad

### Relational Comparison Algorithm

x < y is compared as follows

@anuhosad

1. Convert the operands to primitive using ToPrimitive (hint:  Number) algorithm (for whichever once is not primitive)

2. If both operands are strings, compare their character codes

3. If both operands are not strings, convert the non-string operand to number using ToNumber() algorithm and compare

`"being honest" > "being encouraging"`
```= true

```
`"human" < "Machine"`
```= false

```

"12" > "3"

```= false

```

"10000" > "abc"

```= false

```

100 > "50"

```= true

```

@anuhosad

`"human" < "machine"`
```= true

```

## Hoisting

Hoist = raise (something) by means of ropes and pulleys

Conceptually,  hoisting suggests that variable and function declarations are physically moved to the top of your code

Technically, the variable and function declarations are put into memory during the compile phase, but stay exactly where you typed them in your code.

@anuhosad

## Hoisting (contd...)

A function declaration is hoisted along with its definition

This lets us use it before it is declared in the code

Only variable declarations are hoisted but not their definitions

A function expression is not hoisted

Hence you cannot use it before it is declared

@anuhosad

### Function Declaration vs Expression

=> function foo () {}

=> var foo = function () {};

Declaration

Expression

=> Can call before it is                        declared

=> Only declaration is               hoisted

=> Both declaration and                 function body are hoisted

=> Cannot call before it is                declared

@anuhosad

```var a = 20;

function foo() {
console.log(a);
var a = 10;
}
```

foo();

`Output : undefined`
```var a = 20;

function foo() {
```
```   var a;
console.log(a);
a = 10;
}
```

foo();

@anuhosad

```var a = 20;

function foo() {
console.log(a);
var a = 10;
```
```   return;
function a() {}
}
```

foo();

`Output : f a() {}`
```var a = 20;

function foo() {
```
```   var a;
function a() {}
console.log(a);
a = 10;
return;
}
```

foo();

@anuhosad

var a = 20;

function foo() {
console.log(a);
var a = 10;
return;
var a = function() {};
}

foo();

`Output : undefined`

var a = 20;

function foo() {
var a;
console.log(a);
a = 10;
return;
a = function() {};
}

foo();

@anuhosad

```var a = 1;

function b() {
a = 10;
return;
}

b();

console.log(a);```
`Output : 10`

@anuhosad

```var a = 1;

function b() {
a = 10;
return;
function a() {}
}

b();

console.log(a);```
`Output : 1`
```var a = 1;

function b() {
function a() {}
a = 10;
return;
}

b();

console.log(a);```

@anuhosad

```function parent() {
var foo = "variable";

function foo() {
return 42;
}

return foo();
}

console.log(parent());```
```Output : Uncaught TypeError:
foo is not a function```
```function parent() {
function foo() {
return 42;
}

foo = "variable";

return foo();
}

console.log(parent());```

@anuhosad

```function parent() {
return foo();
function foo() {
return 42;
}
var foo = "variable";
}

console.log(parent());```
`Output : 42`
```function parent() {
function foo() {
return 42;
}
var foo; // ignored
return foo();
foo = "variable";
}

console.log(parent());```

@anuhosad

```var abc = 0;

if (1 === 0){
function a(){
abc = 7;
}
} else if ('a' === 'a'){
function a(){
abc = 19;
}
} else if ('foo' === 'bar'){
function a(){
abc = 'foo';
}
}

a();
console.log(abc);```
`Output : 19`

In an ES2015 environment, a function declaration inside of a block will be scoped inside that block

@anuhosad

let a = 20;
{

console.log(a);

console.log(typeof(a));

let a = 10;

```}​
```
`Uncaught ReferenceError: Cannot access 'a' before initialization`

Temporal Dead Zone

### Hoisting & "let"

@anuhosad

var a = 20;
{

console.log(a);

console.log(typeof(a));

var a = 10;

`}​`
```// 20
```
```// number
```

var a = 20;

function foo() {

console.log(a);

console.log(typeof(a));

var a = 10;

}

foo()

```// undefined
```
```// "undefined"
```

@anuhosad

@anuhosad

@anuhosad

By anupama hosad

• 1,679