Module 2

JS Institute and Edube.org deserve all recognition. Just jotted the ideas I didn't know.

JS Institute

Edube.org

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

"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.

      greeting = "Hello!";
      greeting = greeting + 100;
      console.log(greeting); // -> Hello!100
    
  • constants

    1. need to be simultaneously declared and initialized.

    2. a change in the constant is impossible.

       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).

      let year = 1990;
      console.log(year); // -> 1990
      console.log(1991); // -> 1991
      console.log("Alice"); // -> Alice
    

Scope

Seems LEGB.

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.

If variable is declared using var, mostly it will be global & available throughout the program. Variable declarations using the word var can also be local when they are defined inside the function.

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.

Variable shadowing

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.

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).

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.

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:

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.

Boolean

The value false is always returned for:

  • 0,

  • NaN,

  • empty string,

  • undefined,

  • null

Any other value will result in true being returned.

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.

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.

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.

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

Strings

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.

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.

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

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

String interpolation

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

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.

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.

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

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

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.

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

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.

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.

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).

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.

// 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.

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).

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.

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.

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.

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

There are functions and values related to a specific object. 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.

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"]