# Module 2 : Variables, Data Types, Type Casting, and Comments

[JS Institute](https://openedg.org/js-institute)

[Edube.org](https://edube.org/learn)

After completing Module 2, the student will:

* have the knowledge and skills to work with variables (i.e. naming, declaring, initializing and modifying their values)
    
* understand concepts such as scope, code blocks, shadowing, and hoisting;
    
* know the basic properties of primitive data types such as boolean, number, bigint, undefined, null, and be able to use them;
    
* be familiar with the basic properties of the primitive data type string, including string literals – single or double quotes, the escape character, string interpolation, basic properties and methods;
    
* know the basic properties of complex data types such as Array and Object (treated as a record) and be able to use them in practice.
    

Among JavaScript programmers, the most popular are the following:

* [*JSFiddle*](https://jsfiddle.net/)
    
* [*CodeP*](https://codepen.io/pen/)[*en*](https://jsbin.com/)
    
* [*JsBin*](https://jsbin.com/)
    
* [*Plunker*](https://plnkr.co/)
    

Other tools than minimal trio: a **code editor**, **interpreter**, and **debugger** may also be needed :

* **package managers** – enabling the management of libraries (containing ready-made solutions that we can use in our programs) or components of the development environment (e.g. npm or yarn)
    
* **task runners and module bundlers** – used, in simple terms, to automate the process of software development and merge the resulting code from many files and libraries (e.g. Grunt or Webpack)
    
* **testing framework** – allows for automatic testing of the correctness of our program in search of potential errors (e.g. Mocha, Jasmine, or Jest)
    
* **security analyzers** – as you can guess, used to control the security of our solution (e.g. Snyk, RetireJS, or OWASP Dependency Check)
    

---

For the declarations, we use the keywords `var` or `let` for **variables** and `const` for **constants**.

```javascript
var height; // This is declaration statement.
console.log(height); // -> outputs "undefined"
console.log(weight); // -> Uncaught ReferenceError: weight is not defined
```

# var vs let

* Both are meant **for declaring variables**. Currently, it is **highly recommended to use the word let.**
    
* The keyword `var` comes from the original JavaScript syntax, and the keyword `let` was introduced much later. Therefore, you will find var more in older programs.
    
* One of the basic differences in the use of var and let is that **let prevents us from declaring another variable with the same name** (an error is generated). Using var allows you to re-declare a variable, which can potentially lead to errors in the program execution.
    

# **strict mode**

```javascript
"use strict";

height = 180; // -> Uncaught ReferenceError: height is not defined
console.log(height);
```

At the beginning of our code, we’ve added `"use strict";` to force the interpreter to behave according to modern JavaScript standards.

The sentence `"use strict";` must be placed at the very beginning of the code. It will cause the interpreter to deal with the rest of the code using the modern JavaScript standard.

# Notes

* Variables in the JavaScript language are weakly and dynamically typed i.e variable can store different types of values (int, string, float) at any point in time.
    
* implicit conversion is possible.
    
    ```javascript
    greeting = "Hello!";
    greeting = greeting + 100; // number 100 converted to string
    console.log(greeting); // -> Hello!100
    ```
    
* **constants**
    
    1. need to be simultaneously declared and initialized.
        
    2. a change in the constant is impossible.
        
        ```javascript
        const greeting; // -> Uncaught SyntaxError: Missing initializer in const declaration
        greeting = "Hello!";
        
        const greeting = "Hello!";
        greeting = "Hi!"; // -> Uncaught TypeError: Assignment to constant variable.
        ```
        
* **Literals**
    
    In this example, we declare the variable year and immediately initiate it with the value 1990. The digits 1990, written in the code at the place of variable initialization, are **a literal** that represents a number.
    
    Then we display on the console the value 1991 and "Alice", in both cases using literals (representing a **number** and a **string** respectively). In JavaScript, almost each data type has its own literal.
    
    ```javascript
    let year = 1990;
    console.log(year); // -> 1990
    console.log(1991); // -> 1991
    console.log("Alice"); // -> Alice
    ```
    

# Scope

Seems **LEGB**.

Unfortunately, the scopes of variables (and constants) declared with `let` and `const` look slightly different than those declared with var.

* Any variable or constant declared using `let` or `const`, can percolate inside nested code blocks. They can't expand their scope outside the block in which they were defined.
    
    ```javascript
    // What happens if we declare something using let or const
    // inside a block?
    
    let height = 180;
    {
        let weight = 70;
        console.log(height); // -> 180
        console.log(weight); // -> 70   
    }
    console.log(height); // -> 180
    console.log(weight); // -> Uncaught ReferenceError: weight is not defined
    ```
    
* If you declare with `var` inside a block, then... well, it will usually turn out to be global again. Both variables, `height` and `weight`, turn out to be global. Will the variables declared using `var` always, regardless of the place of declaration, be global? Definitely not. The problem is that var ignores ordinary program blocks, treating them as if they do not exist.
    
    ```javascript
    var height = 180;
    {
        var weight = 70;
        console.log(height); // -> 180
        console.log(weight); // -> 70   
    }
    console.log(height); // -> 180
    console.log(weight); // -> 70
    ```
    
    So if variable is declared using `var`, **mostly** it will be global & available throughout the program. Variable declarations using the word `var` can **only** be local when they are defined inside the function.
    
    ```javascript
    var globalGreeting = "Good ";
    
    function testFunction() {
        var localGreeting = "Morning ";  
        console.log("function:");
        console.log(globalGreeting);
        console.log(localGreeting);
    }
    
    testFunction();
    
    console.log("main program:");
    console.log(globalGreeting);
    console.log(localGreeting); // -> Uncaught ReferenceError: localGreeting is not defined
    ```
    

Variables declared with the `let` keyword are local inside the code block (i.e. inside the range limited by curly brackets), while variables declared with the `var` keyword are local inside the function block.

So if you declare a variable inside a function block, whether using `let` or `var`, it will only be visible (and usable) inside the function block. This is very useful, because usually the variables you use inside a function are not needed outside of it.

## **Variab**le sh**adowing**

It means that we can declare a global variable and a local variable of the same name.

In the local scope, in which we **declare** a local variable using its name, we will have access to the local value (the global variable is hidden behind the local one, so we do not have access to it in this local scope). Using this name outside the local scope means that we will be referring to the global variable. **This is not best programming practice, however, and try to avoid giving the same variable names to multiple variables, regardless of where you declare them.**

```javascript
let counter = 100;   // same with var
console.log(counter); // -> 100
{
  let counter = 200;
  console.log(counter); // -> 200
}
console.log(counter); // -> 100
```

## Variable Hoisting

A good practice is always to declare variables before they are used but the original JavaScript syntax allows for some deviations from this rule. The JavaScript interpreter scans the program before running it.

**It searches for all variable declarations and moves them to the beginning of the range in which they were declared** (to the beginning of the program if they are global, to the beginning of the block if it is a local `let` declaration, or to the beginning of the function if it is a local `var` declaration).

```javascript
var height = 180;
console.log(height);  // -> 180
console.log(weight);  // -> Uncaught ReferenceError: weight is not defined
```

In the above example, we forgot to declare the variable weight. The result is obvious: we’re referring to a variable (that is, we’re trying to read its contents) which does not exist.

```javascript
var height = 180;
console.log(height);  // -> 180
console.log(weight);  // -> undefined
var weight = 70;
console.log(weight);  // -> 70
```

Here, Hoisting has worked, and the declaration has been moved by the interpreter to the beginning of the range (in this case the program).

However, the attempt to display the contents of the weight variable give two different results. Why? Hoisting only concerns the declaration, not initialization. So the value 70, which we assign to the weight variable, remains on the line where the original declaration is. The above example is interpreted by the JavaScript engine more or less in the following way:

```javascript
var weight;
var height = 180;
console.log(height);  // -> 180
console.log(weight);  // -> undefined
weight = 70;
console.log(weight);  // -> 70
```

Hoisting unfortunately works a little differently with the `let` and `const` declarations.

**Most of all, you will remember always TO DECLARE VARIABLES BEFORE USING THEM.**

# Summary 2.1

* Remember to declare variables before using them.
    
* Pay attention to where you declare them – whether they are local or global.
    
* Try to use the keywords let and const, not the word var. Knowing the latter will be useful not for understanding the examples found in various sources, but avoid using it yourself.
    
* Remember not to use the same names for different variables, even if you declare them in different ranges.
    

# Primitive data types

There are six primitive (or simple) data types: **Boolean, Number, BigInt, String, Symbol**, and **undefined**. Additionally, the primitive **null** value is also treated as a separate type. The primitive is a type of data whose values are atomic, in other words, it will not be possible to extract components from it.

All possible return values of the `typeof` operator are:

`"undefined" "object" "boolean" "number" "bigint" "string" "symbol" "function"`

## Boolean

The value `false` is always returned for:

* `0`,
    
* `NaN`,
    
* empty string,
    
* `undefined`,
    
* `null`
    

Any other value will result in `true` being returned.

```javascript
console.log(Boolean(true)); // -> true

console.log(Boolean(42)); // -> true
console.log(Boolean(0)); // -> false
console.log(Boolean(NaN)); // -> false

console.log(Boolean("text")); // -> true
console.log(Boolean("")); // -> false

console.log(Boolean(undefined)); // -> false

console.log(Boolean(null)); // -> false
```

## Number

Represents both real numbers (e.g. fractions) and integers.

```javascript
let a = 10; // decimal - default 
let delayInSeconds = 0.00016; // fraction
let b = 0x10; // hexadecimal 
let c = 0o10; // octal 
let d = 0b10; // binary 
 
console.log(a); // -> 10 
console.log(b); // -> 16 
console.log(c); // -> 8 
console.log(d); // -> 2 

let x = 9e3; // exponential form
let y = 123e-5; // exponential form
console.log(x); // -> 9000
console.log(y); // -> 0.00123
```

We use three additional special values, which are: `Infinity`, `-Infinity` and `NaN` (not a number).

`NaN`, is not so much a numerical value but a notification that some arithmetic action (or mathematical function) could not be performed because the argument is either not a number, or cannot be converted to a number.

```javascript
let a = 1 / 0;
let b = -Infinity;

console.log(a); // -> Infinity
console.log(b); // -> -Infinity
console.log(typeof a); // -> number
console.log(typeof b); // -> number

let s = "it's definitely not a number";
let n = s * 10;
console.log(n); // -> NaN
console.log(typeof n); // -> number
```

## BigInt

It allows us to write integers of virtually any length.

As the BigInt is an integer type, the division result will always be **rounded down** to the nearest whole number.

BigInt literals are numbers with the **…n** suffix.

You cannot use other types in arithmetic operations on BigInts.

The BigInt does not have its own equivalent of `Infinity` or `NaN` values. In the case of the BigInt type, such actions will generate an error.

```javascript
let big = 1234567890000000000000n;
let big2 = 1n;

console.log(typeof big); // -> bigint

console.log(big2); // -> 1n
console.log(7n / 4n); // -> 1n

let big3 = 1000n + 20; // -> Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions

let big4 = 1000n / 0n; // -> Uncaught RangeError: Division by zero
```

## Strings

**Strings**, like other primitives, are immutable, so when we want to change even one letter in a string, in reality, we create a new string.

Seeing the arithmetic operators `-`, `*`, or `\`, the JavaScript interpreter tries to interpret the given values as numbers, or convert them into numbers. So if the character strings consists of digits, the automatic conversion will be successful and we will get the result of the arithmetic action as a Number type value. If the character string cannot be interpreted as a number (and converted) we will get the NaN result.

```javascript
let path = "C:\\Windows" - "Windows";
console.log(path); // -> NaN

let test = "100" - "10";
console.log(test); // -> 90
console.log(typeof test); // -> number
```

The exception is the addition operation, which will not be treated as an arithmetic one, but as an attempt to create a new string by combining two input strings.

```javascript
let path = "C:\\" + "Windows";
console.log(path); // -> C:\Windows

let test = "100" + "10";
console.log(test); // -> 10010
console.log(typeof test); // -> string
```

### **String interpolation**

Such a literal is created using backticks `` ` `` instead of quotation marks.

The places where values are inserted are marked with curly brackets preceded by a `$` sign.

```javascript
let country = "Malawi";
let continent = "Africa";
 
let sentence = `${country} is located in ${continent}.`;
console.log(sentence); // -> Malawi is located in Africa.
```

## Undefined

The undefined type has only one value: `undefined`. It’s the default value that all variables have after a declaration if no value is assigned to them. You can also assign the value `undefined` to any variable, but in general, this should be avoided, because if we need to mark a variable as not holding any meaningful value, we should use `null`.

```javascript
Let declaredVar;
console.log(typeof declaredVar); // -> undefined

declaredVar = 5;
console.log(typeof declaredVar); // -> number

declaredVar = undefined;
console.log(typeof declaredVar); // -> undefined

The undefined value can also be returned by the typeof operator when a non-existent variable is used as an argument.

Console.log(typeof notDeclaredVar); // -> undefined
console.log(notDeclaredVar); // -> Uncaught ReferenceError: notDeclared is not defined
```

## Symbol

It’s a new primitive type that was added to JavaScript in 2015. It doesn't have any literal value, and can only be created using a special constructor function. Symbols are a form of identifier that are guaranteed to be unique.

## null

The value itself is a primitive, while the type to which it belongs is not a primitive type, such as Number or undefined. When checked with the `typeof` operator for `null` , it will return `"object"` .

The `null` value is used to indicate that the variable right now does not contain anything, and most often it is a variable that is intended to contain values of complex types.

we can assume that the `undefined` value is assigned to uninitialized variables automatically, but if we want to explicitly indicate that the variable does not contain anything, we assign it a `null` value.

```javascript
let someResource;
console.log(someResource); // -> undefined
console.log(typeof someResource); // -> undefined

someResource = null;
console.log(someResource); // -> null
console.log(typeof someResource); // -> object
```

## Type conversions

### Primitive construction functions

The following functions will return primitives of a given type: `Boolean`, `Number`, `BigInt`, and `String`.

Most of these functions can be called without any arguments. In such a situation:

* the function `String` will by default create and return an empty string – primitive "";
    
* the function `Number` will by default create and return the value 0;
    
* the function `Boolean` will by default create and return the value of false.
    

The function `BigInt`, unlike other constructor functions, requires you to pass some initial value to it. This can be an integer number that will be converted to a BigInt (see examples).

```javascript
const str = String();
const num = Number();
const bool = Boolean();

console.log(str); // ->
console.log(num); // -> 0
console.log(bool); // -> false

const big1 = BigInt(42);
console.log(big1); // -> 42n

const big2 = BigInt();   // -> Uncaught TypeError: Cannot convert undefined to a BigInt
```

But creating default values is not impressive at all. We can accomplish these using literals. So what do we use these functions for? Well, we use them in type conversions.

### Explicit conversions

A `BigInt` can also be converted to a `Number`, but we need to remember that a BigInt can store much bigger values than a Number, so for large values, part of them can be truncated or end up being imprecise.

```javascript
console.log(Number(42)); // -> 42

console.log(Number("11")); // -> 11
console.log(Number("0x11")); // -> 17
console.log(Number("0o11")); // -> 9
console.log(Number("0b11")); // -> 3
console.log(Number("12e3")); //  -> 12000
console.log(Number("Infinity"));// -> Infinity
console.log(Number("text")); // -> NaN : any string that cannot be converted 

console.log(Number(14n)); // -> 14
console.log(Number(123456789123456789123n)); // - >  123456789123
456800000

console.log(Number(true)); // -> 1
console.log(Number(false)); // -> 0

console.log(Number(undefined)); //  -> NaN

console.log(Number(null));// -> 0
```

Unlike other conversions, **conversion to a BigInt** will throw an error, and will stop the program when unable to convert a given value. Please pay attention to the fact that the first error prevents further code execution.

```javascript
console.log(BigInt(11)); // -> 11n
console.log(BigInt(0x11)); // -> 17n
console.log(BigInt(11e2)); // -> 1100n

console.log(BigInt(true)); // -> 1n

console.log(BigInt("11")); // -> 11n
console.log(BigInt("0x11")); // -> 17n

console.log(BigInt(null)); // -> Uncaught TypeError: Cannot convert null to a BigInt

console.log(BigInt(undefined)); // -> Uncaught TypeError: Cannot convert undefined to a BigInt

console.log(BigInt(NaN)); // -> Uncaught RangeError: The number NaN cannot be converted to a BigInt because it is not an integer
```

### Implicit conversions

Conversions can also happen automatically, and they happen all the time.

```javascript
const str1 = 42 + "1"; // when one of the arguments is a string, JavaScript will convert the rest of the arguments to a string
console.log(str1);        // -> 421
console.log(typeof str1); // -> string

const str2 = 42 - "1"; // Subtraction with a string, doesn't make much sense. So JavaScript converts everything to Numbers.
console.log(str2);        // -> 41
console.log(typeof str2); // -> number
```

# Autoboxing

All data of primitive types such as Number, BigInt, Boolean, or String have corresponding objects to which they can be converted. Each of these objects will have methods designed for a specific data type.

If a dot appears after a literal representing a primitive type, or after a variable containing this type of data, the JavaScript interpreter tries to treat this value as an object and not a primitive. For this purpose, it converts the primitive to the corresponding object on the fly, which has the appropriate methods (i.e. it performs autoboxing).

```javascript
let river = "Mekong";
let character = river.charAt(2);
console.log(character); // -> k
```

In the variable `river`, we store the primitive of a String type. In the next line, we refer to this variable, writing a dot after its name and the name of one of the methods – `charAt` (a method of the String class object). Although the primitive has no methods that can be called, the interpreter temporarily converts this value to a suitable object that already has such methods. One of these methods is `charAt` .

After the operation is completed, the interpreter removes the temporary object. So from our point of view, it looks like we just called a method on a given primitive type.

# **Complex data types**

May consist of multiple elements, each of which may be of a primitive or composite type.

Two of the complex types : objects and arrays.

## Object

A **record** is a collection of named fields. Each field has its own name (or key) and value assigned to it. In the case of JavaScript objects, these fields are usually called properties.

It allow you to store multiple values of different types in one place.

What do we need objects for? The simplest reason for using them may be the desire to store several values in one place, which are linked to each other for some reason.

```javascript
// empty object
let testObj = {}; 
console.log(typeof testObj); // -> object

// defining an object containing two fields with keys nr and str
let testObj = {
    nr: 600,
    str: "text"
};
```

We can also modify the whole object by adding a new, previously non-existent property. We also do this using dot notation – if during an attempt to modify the property the interpreter does not find the key we specify, it will create it.

If you can add new fields to an existing object, can you also delete them? Of course you can: the delete operator is used for this.

```javascript
let user1 = {
    name: "Calvin",
    surname: "Hart",
    age: 66,
    email: "CalvinMHart@teleworm.us"
};

console.log(user1.age); // -> 66
user1.age = 67;
console.log(user1.age); // -> 67

console.log(user2.phone); // -> undefined
user2.phone = "904-399-7557";
console.log(user2.phone); // -> 904-399-7557

console.log(user2.phone); // -> 904-399-7557
delete user2.phone;
console.log(user2.phone); // -> undefined
```

## Array

The stored data (the values) can be of **any type**. The difference between these structures is that in an array we only store values, without the associated names (i.e. the keys).

```javascript
let emptyArray = []; 
console.log(emptyArray[0]); // -> undefined

let values = ["Test", 7, 12.3, false];
```

What's interesting is that we don't have to fill the array with elements one by one – you can leave empty spaces in it.

```javascript
let animals = [];
console.log(animals[0]); // -> undefined

animals[0] = "dog";
animals[2] = "cat";

console.log(animals[0]); // -> dog
console.log(animals[1]); // -> undefined
console.log(animals[2]); // -> cat
```

Array elements can be any data, including another array, objects.

```javascript
let names = [["Olivia", "Emma", "Mia", "Sofia"], ["William", "James", "Daniel"]];
console.log(names[0][1]); // -> Emma
console.log(names[1][1]); // -> James
let femaleNames = names[0];
let malesNames = names[1];

let users =[ 
    {
        name: "Calvin",
        surname: "Hart",
        age: 66,
        email: "CalvinMHart@teleworm.us"
    },
    {
        name: "Mateus",
        surname: "Pinto",
        age: 21,
        email: "MateusPinto@dayrep.com"
    }
];

console.log(users[0].name); // -> Calvin
console.log(users[1].age); // -> 21
```

Apply the `typeof` operator to the variable containing the array. **In JavaScript, everything except primitives is an object.**

Arrays are also treated as a special kind of object. The `typeof` operator does not distinguish between object types (or more precisely, classes), so it informs us that the days variable contains an object. If we would like to make sure that the variable contains an array, we can do it using the `instanceof` operator.

```javascript
let days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
let day = "Sunday";

console.log(typeof days); // -> object
console.log(typeof day); // -> string

console.log(days instanceof Array); // -> true
console.log(day instanceof Array); // -> false
```

## method and property

Now it turns out that an array is implemented as an object in JavaScript, so it probably also has its methods and properties.

The *length* is a property and not a method.

The `slice, concat` method allows you to create a new array from selected elements of the original array. Calling the method does not affect the original array.

```javascript
let names  = ["Olivia", "Emma", "Mateo", "Samuel"];
console.log(names.length); // -> 4

console.log(names.indexOf("Mateo"));
names.push("Amelia"); //  append it to the right end of the array.
names.unshift("Javed"); // new element is added to the beginning of the array.
let name = names.pop(); // remove the last element from the array.
name = names.shift(); // remove the element from the beginning of the array.
names.reverse();

let n1 = names.slice(2);
console.log(n1); // -> ["Mateo", "Samuel"]

let n2 = names.slice(1,3);
console.log(n2); // -> ["Emma", "Mateo"]

let n3 = names.slice(0, -1);
console.log(n3); // -> ["Olivia", "Emma", "Mateo"]

let n4 = names.slice(-2);
console.log(n4); // -> ["Samuel", "Mateo"]

console.log(names); // -> ["Olivia", "Emma", "Mateo","Samuel"]

let otherNames = ["William", "Jane"];
let allNames = names.concat(otherNames);
console.log(allNames); // -> ["Olivia", "Emma", "Mateo", "Samuel", "William", "Jane"]
```
