Abílio Azevedo.

JavaScript

Cover Image for JavaScript
Abílio Azevedo
Abílio Azevedo

History of Javascript

Tim Berners Lee in 1989 created the World Wide Web - Server Communication -> Browser Nexus.

Timeline of Web Browsers

Timeline of web browsers

1994 - Marc Andressen, creator of NCSA Mosaic launched the Netscape browser;

1995 - Brendan Eich was recruited to write a programming language for the Netscape 2.0 browser. So he implemented the JavaScript language (initially called Mocha and then Livescript, but due to influences from Java was called Javascript) in 10 days in May 1995, using Java, Scheme, Self as a base and with some influences from Perl;

netscape-navigator-2-01-01

1996 - Microsoft with Internet Explorer copied Javascript and launched JScript;

1997 - Netscape, afraid of Jscript, standardized the JavaScript language with ECMA International, changing the name to ECMAScript;

With the standardization, a committee called TC39 was formed to work on the ECMA-262 specification

ES1 - 1997 - 110 pages - It was created just for the browser

ES2 - 1998 - 117 pages - Compliance with ISO/IEC 16262 regulations

ES3 - 1999 - 188 pages - Exception Handling (throw/try/catch), Regular Expression, switch, do-while

In 2005 with AJAX (Asynchronous JavaScript And XML) and the use of JS by servers, the language got a bad reputation

Microsoft and Yahoo were working on V3.1 and Adobe, Mozilla, Opera and Google on V4. The projects became very distant and were rejected by TC39.

ES5 - 2009 - 252 pages - JSON, strip mode, reserved words as property keys, multiline string, Object API, Array.prototype.*

ES6 - ECMA2015 - 566 pages - Class, Arrow Function, Proxy, Reflect, Map, Set, Destructuring, Rest Parameter, Default Value, Template Literal, Spread Operator, Generators, Promises, Modules

ES7 - ECMA2016 - 586 pages - Array.prototype.includes, Exponentiation operator...

ES8 - ECMA2017 - 885 pages - Async/Await, Object.values, Object.entries, String.prototype.padStart, String.prototype.padEnd, Trailling commas in parameters list, objects and arrays.

ECMA Script Versions

ECMA Script compability

ECMA Script Compatibility

Javascript Concepts

Variables

  • Declaration: The variable name is registered in the execution context, also known as scope, of the function

  • Initialization: The variable is initialized with the undefined value

  • Assignment: A value is assigned to the variable

VAR: When using var, the variable is declared and initialized in the function scope, not respecting block and allowing redeclaration and reassignment

LET: When using let, the variable is declared in the function scope but is only initialized later, respecting block and allowing reassignment but not redeclaration

CONST: When using const, the variable is declared in the function scope but is only initialized later, respecting block and not allowing reassignment or redeclaration.

Be careful when declaring a variable without VAR, LET, CONST because it goes into the global scope:

(function () {
    pi = 3.141592;  
})();

console.log(pi); 

Variable Identifiers: A valid identifier must start with [a-zA-Z_$] followed by [a-zA-Z0-9_$]:

let name123;
let Name123; 
let $name123;
let _name123;

Naming Conventions:

  • Camel Case: myVariable, myMethod, myClass
  • Pascal Case: MyVariable, MyMethod, MyClass
  • Kebab Case: my-variable, my-method, my-class
  • Snake Case: my_variable, my_method, my_class

Data Types:
undefined, null, boolean, string, symbol, number and object

Objects

They are collections of properties (attributes and methods) that consist of key-value pairs

They are created using literal notation or constructor functions like new Object()

They are dynamic and properties can be changed at any time

const person = {
  name: "Maria",
  age: 20
}

Symbols

The Symbol type is primitive, unique and immutable, acting as a unique key in an object.

Symbol("a") === Symbol("a") // false

Functions

Everything is based on functions. Instantiating with new or (). Object is a set of key/value

Anonymous functions:
They are functions that are dynamically declared at runtime. They are called anonymous functions because they do not receive a name like traditional functions.

const myfn = function(param1, param2) {
  return param1 + param2;
};

myfn(10, 20); // returns 30

Generator functions:
Return a sequence of results

Generator Function

A generator function is marked by the signature with an asterisk (*) and can contain one or more calls using yield (like the "return value" of a function) as in the example below:

function* myFn() {
  yield 'result01'
  yield 'result02' 
}

Classes

  • They are "templates" that encapsulate data and behaviors;

  • They are created using a class declaration and the class keyword;

  • They usually follow the OO paradigm, with inheritance, polymorphism, etc;

  • Once created, it cannot be dynamically changed;

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

const maria = new Person("Maria", 20);  

Class keywords:

  • Public: Public interface to be coupled. These class members are available to all who can access the class instance (owner).

  • Private: These members are only accessible in the class that instantiated the object. Reduce coupling (not exposing too many implementation details)

  • Protected: This keyword allows a little more access than private members, but much less than public. A protected member is accessible within the class (similar to private) and in any object that inherits from it. A protected value is shared by all layers of the prototype chain. It is not accessible to anyone else.

Prototype: We can modify the functionality of the function/object/variables.

function Person(first, last, age, eyecolor) {
  this.firstName = first; 
  this.lastName = last;
  this.age = age;
  this.eyeColor = eyecolor; 
}

Person.prototype.name = function() {
  return this.firstName + " " + this.lastName;
};

Date

The ISO 8601 standard establishes a standard for representing dates as string formats. Below I will present some formats according to the ISO 8601 standard and what happens when we use them to instantiate the Date class object in JavaScript.

console.log(new Date('2021-12-15'));
// Output: Date Tue Dec 14 2021 21:00:00 GMT-0300 (Brasilia Standard Time)  

When we do not specify the time, minute or, optionally, the second, the ISO standard establishes that the time zone of the date is considered UTC (Coordinated Universal Time)

console.log(new Date(2021-12-15T00:00Z'));
// Output: Date Tue Dec 14 2021 21:00:00 GMT-0300 (Brasilia Standard Time)

The output is the same as the previous code. The difference is that we specify the hour and minute and, by putting the uppercase Z character at the end, we are explicitly stating that the time zone is UTC.

console.log(new Date('2021-12-15T00:00')); 
// Output: Date Wed Dec 15 2021 00:00:00 GMT-0300 (Brasilia Standard Time)

When we specify the hour, minute and, optionally, the second, the standard defines that it is assumed that if the date and time refers to the local time zone.

Array

.forEach: Iterates over the elements of an array:

array.forEach(el => {
  // do something with el
});

.filter: Filters an array based on a condition, returning another array:

const filtered = array.filter(el => el > 2);

.map: Maps values to a new array from an existing array:

const doubled = array.map(el => el * 2); 

.reduce: Reduces an array to a single value accumulating the elements through a function:

const sum = array.reduce((accumulator, el) => accumulator + el, 0);  

.sort: Sorts array elements in-place:

array.sort((a, b) => a - b); // ascending 
array.sort((a, b) => b - a); // descending

Array methods cheatsheet

Comparison Operators

  • Equal (==) - Returns true if the values of the two operands are equal.
x == y
  • Not equal (!=) - Returns true if the values of the two operands are different.
x != y  
  • Greater than (>) - Returns true if the left operand is greater than the right one.
x > y
  • Less than (<) - Returns true if the left operand is less than the right one.
x &lt; y
  • Greater than or equal to (>=) - Returns true if the left operand is greater than or equal to the right one.
x &gt;= y
  • Less than or equal to (<=) - Returns true if the left operand is less than or equal to the right one.
x &lt;= y

These operators compare two values and return a boolean result (true or false) that can be used in conditional and loop structures.

The main difference between the == and === operators

  • The == operator compares only the value of the operands, performing type coercion if necessary. For example:
5 == "5" // returns true 

Here JavaScript converts the string "5" to number before comparison.

  • The === operator compares value and type of the operands. It's a stricter comparison. Example:
5 === "5" // returns false

Since one operand is number and the other is string, it returns false even though both have the "5" value.

In summary:

  • == compares values performing type coercion
  • === compares values and types without conversion

So in general using === is preferable to avoid unexpected behaviors. But sometimes the == coercion can also be useful.

Arithmetic Operators

Addition + Subtraction - Multiplication * Division / Remainder %

Assignment Operators

Addition += Subtraction -= Multiplication *= Division /= Remainder %=

Binary Operators

Or: | And: & Exclusive Or (XOR): ^ Negation (not): ~ Left shift << Basically it's a multiplication by 2 multiplied by the shift value.

> 4 << 2  
16
> (4).toString(2).padStart(32,0)
'00000000000000000000000000000100' 
> (16).toString(2).padStart(32,0)
'00000000000000000000000000010000'

Right shift >> Basically it's a division by 2 multiplied by the shift value.

> 128 >> 1
64 
> (128).toString(2).padStart(32,0)
'00000000000000000000000010000000'
> (16).toString(2).padStart(32,0) 
'00000000000000000000000001000000'

Right shift with sign change >>>

Other Operators

&& Operator for IF: The && (logical AND) operator evaluates the left expression and only evaluates the right expression if the left one is truthy. This allows for conditional returns like:

function foo(algo) {
  return algo && algo.method();  
}

|| Operator for IF: The || (logical OR) operator evaluates the left expression and only evaluates the right expression if the left one is falsy. This allows for conditional returns like:

function foo(algo) {
  return algo.method2() || algo.method();
}

For: Loop statement to iterate over data structures:

for(let i = 0; i < array.length; i++) {
  // do something
}

Spread operator: The spread (...) operator "spreads" the properties of an object or the elements of an array into another one. Useful for cloning and merging:

// Cloning  
const newArray = [...originalArray];  
const newObject = {...originalObject};   

// Merging arrays
const mergedArray = [...array1, ...array2];  
const mergedObject = {...object1, ...object2};

Destructuring operator: Allows extracting array values or object properties into distinct variables:

// Array
const [a, b] = [10, 20];

// Object   
const {prop1, prop2} = {prop1: 10, prop2: 20}; 

Numeric conversions

Not all numeric operators perform the desired (type coercion) conversion:

&gt; &quot;10&quot; + 0   
&quot;100&quot;
&gt; &quot;10&quot; - 5  
5  

Some conversions can lose information:

&gt;parseInt(&quot;9.9&quot;,10)
9  
&gt;parseFloat(&quot;0xFF&quot;)  
0
&gt;parseFloat(&quot;0b10&quot;)
0  

IEEE 754

IEEE 754 is a numerical representation standard created in 1985 and adopted by several programming languages like JavaScript, Ruby, Python and Java. IEEE 754 Single Floating Point Format.svg

Rounding errors can occur:

&gt; 0.1 + 0.2  
0.30000000000000004
&gt; 666.7 - 666.6
0.10000000000002274  
&gt; 33.3 * 3   
9989999999999999
&gt; 12.2 / 0.1
121.99999999999999  

You can check on the IEEE 754 calculator.

Infinity, which can be positive or negative, is returned when an operation exceeds the number type limits.

&gt; 1/0;   
Infinity  
&gt; Math.pow(10, 1000);   
Infinity
&gt; Number.MAX_VALUE * 2
Infinity  
&gt; Math.log(0);  
-Infinity
&gt;-Number.MAX_VALUE * 2   
-Infinity  

NaN, or Not a Number, is returned when we perform a numeric operation where the result cannot be determined. It does not throw an error, continues the flow.

> 10 *"Javascript";   
NaN  
> 0/0;
NaN   
> Math.sqrt(-9);
NaN   
> Math.log(-1);  
NaN
> parseFloat("JavaScript");  
NaN

String

In JavaScript, there are two main types of strings:

  1. String literal ("normal" string): Strings created by placing text between single quotes (&#39;&#39;) or double quotes (""``). For example:
let string1 = 'This is a string';
let string2 = "Also a string"; 

String literals are the most common string types in JavaScript.

  1. String Object: The String object in JavaScript provides additional methods and properties to work with strings. For example:
let s = new String("Hello World");
console.log(s.length); // 11

Here we create a String object using the String constructor and then access the length property of that object.

The main difference is that string literals are more straightforward, while the String object allows for extended functionality.

But in most cases, string literals are sufficient, with String object usage being less common. Some key differences:

  • String literals have better performance
  • String objects can have methods added and behaviors modified, while string literals are more immutable.

So in summary, for most purposes, focusing on using string literals is ideal for working with strings in JavaScript. The String object is more advanced and rarely necessary.

String literal String Object

let counter = 0;
console.time("performance"); 
while(counter < 100000){
  new String("JavaScript");
  counter++;
}
console.timeEnd("performance");


let counter = 0;
console.time("performance"); 
while(counter < 100000){
  "JavaScript";
  counter++;
}
console.timeEnd("performance");
    
performance: 5.56884765625 ms performance: 1.032958984375 ms

Math

Math is a global object that contains mathematical constants and methods for performing number-related operations.

> Math.E;
2.718281828459045  
> Math.LN10; // Natural logarithm of 10  
2.302585092994046  
> Math.LN2; // Natural logarithm of 2
0.6931471805599453
> Math.LOG10E; // Base 10 logarithm of E 
0.4342944819032518
> Math.LOG2E; // Base 2 logarithm of E
1.4426950408889634  
> Math.PI;  
3.141592653589793
> Math.SQRT1_2; // Square root of 1/2  
0.7071067811865476
> Math.SQRT2; // Square root of 2
1.4142135623730951
  • abs: Converts the number's sign to positive;
  • ceil: Rounds the number up;
  • floor: Rounds the number down;
  • round: Rounds the number up if the decimal part is from 5 to 9 and down if it is from 0 to 4;
  • sign: Returns 1 if the number is positive and -1 if negative;
  • trunc: Eliminates the number's decimal part, making it an integer;
  • min: Returns the smallest number passed as a parameter
> Math.min(1,2,3,4,5,6)  
1
  • max: Returns the largest number passed as a parameter
> Math.max(1,2,3,4,5,6)    
6
  • random: Returns a random number between 0 and 1, not including 1

Regex

Regular expressions are structures formed by a sequence of characters that specify a formal pattern that is used to validate, extract or even replace characters within a String.

We have two ways to represent a regular expression:

let regExp1 = new RegExp("john@gmail.com")

let regExp2 = /john@gmail.com/;

We can test:

let result = regExp.test("john@gmail.com");
console.log(result); //true

We can execute:

let result = regExp.exec("john@gmail.com");
console.log(result); //['john@gmail.com', index: 0, input: 'john@gmail.com', groups: undefined]

Metacharacters: are characters with specific functions, that inform patterns and positions impossible to be specified with normal characters.

^ - Starts with a certain character

$ - Ends with a certain character

\ - The backslash is used before special characters, in order to escape them

[abc] - Accepts any character inside the group, in this case a, b and c

[!abc] - Does not accept any character inside the group, in this case a, b or c

[0-9] - Accepts any character between 0 and 9

[^0-9] - Does not accept any character between 0 and 9

\w - Represents the set [a-zA-Z0-9_]

\W - Represents the set [^a-zA-Z0-9_]

\d - Represents the set [0-9]

\D - Represents the set [^0-9]

\s - Represents a whitespace

\S - Represents a non-whitespace

\n - Represents a newline

\t - Represents a tab

Quantifiers can be applied to characters, groups, sets or metacharacters.

{n} - Quantifies a specific number

{n,} - Quantifies a minimum number

{n,m} - Quantifies a minimum and maximum number

? - Zero or one

*- Zero or more

+- One or more

Capture Groups () - Determine a capture group to extract values from a given String

this

“This” can vary -> In arrow function this is fixed Depends on the lexical context, this is related to the context.

function f1() { console.log(this == window)}
function f2() { console.log(this == body)}  
const f3 = () => console.log(this==window) 

Bind: Forces a certain context for this. Useful when the correct context of this is lost:

In JavaScript, just glancing, it's hard to guess the ".this" context of functions. This is because they can be executed in the future and this is the biggest cause of those "undefined is not a function" errors.

Contrary to what many say, the solution is simpler than you can imagine. What you need to keep in mind is: who will execute that function in the future?

A great example is the browser's "click" event, let's take a look at this scenario:

const instance = {
    name: 'test',   
    myOnClick() {
      console.log('name', this.name)
   }  
}
window.addEventListener('click', instance.myOnClick)

The myOnClick function of the instance object will be triggered in the future and will inherit the "this" of window, as the addEventListener is part of the window context. Therefore, the "name" property will not exist and we will have an "undefined" result.
That's where the .bind function comes in. With it, you set, exactly, where the function will inherit the this context from. So, to solve the problem, just manually "bind" the instance as the context:

window.addEventListener('click', instance.myOnClick.bind(instance)) 

This works for class cases, objects and especially for when you need to convert a callback to Promise in Node.js, something like:

await util.promisify(fs.write.bind(fs))(...args)

Tagged templates

Tagged templates (or template tags) are an advanced JavaScript feature that allows processing template literal values ​​with a custom function.

The function used to process the template literal is called a "tag function". It receives as parameters the static strings of the template and the interpolated values.

function format(parts, ...values) {
  const formatted = values.map(value => {
    if(typeof value === 'number') {
      return value.toLocaleString('en-US');  
    }

    return value;
  });

  return parts.reduce((str, part, i) => {
    return `${str}${part}${formatted[i] || ''}`;
  }, '');  
}

const price = 1000.99;
const message = format`The price is $ ${price}`;

console.log(message); // The price is $ 1,000.99  

In this way, the custom format tag applies the appropriate formatting to the interpolated values ​​in the template literal.

This greatly facilitates reusing and standardizing string output rules in an application.

Asynchronicity

Asynchronicity in JavaScript allows code to execute in a non-blocking way (since javascript is single threaded), improving performance and user experience. However, just returning a Promise does not automatically make the code asynchronous.

Let's look at an example:

console.log("Before the asynchronous function"); 

async function loopMillionsOfTimes() {
  for(let i = 0; i < 1000000; i++) {
    // some simple task
  }
  console.log("Loop finished");
}

loopMillionsOfTimes().then(() => 
  console.log("Promise returned")
);

console.log("After the asynchronous function");

The result will be:

Before the asynchronous function
Loop finished  
After the asynchronous function
Promise returned

Despite returning a Promise, the entire loop will block execution of the code until it finishes. This is because we are not using any internal asynchronous JavaScript APIs.

To really make this asynchronous, we can use setTimeout:

console.log("Before the asynchronous function");

async function asyncLoop() {
  return new Promise((resolve)=> setTimeout(() => {
    for(let i = 0; i <= 1000000; i++){
      // something simple
    }
    // long loop
    console.log("Loop finished");
    resolve();
  }, 0))

}

asyncLoop().then(() =>
  console.log("Promise returned")
);

console.log("After the asynchronous function");

The result will be:

Before the asynchronous function
After the asynchronous function 
Loop finished
Promise returned

This way, the loop will be scheduled for future execution, allowing the code to continue normally.

Other common asynchronous APIs in JavaScript include fetch, requestAnimationFrame, setInterval and many more. Using them properly, we can write high-performance asynchronous code in JavaScript.

Compilers

Babel is a JavaScript compiler. It is used to convert ES6 code into a compatible JS version for different environments.

Not everything supports ES6. If you are using modern syntax like React, you will need Babel on hand.

We hope all browsers support ES6 one day. But for now, to make sure everything works perfectly, Babel can't be missing from your project.

The module folding or tree-shaking is an optimization technique that removes unused Javascript code during the construction of an application before putting it into production.

It analyzes application files to identify unused exports and imports from Javascript modules. Then it eliminates those unreferenced features, reducing the size of the compiled Javascript package without affecting functionality.

In this way, tree-shaking removes "dead code" and leaves only the resources actually needed to run the application, improving loading performance and memory usage. It is very useful in modern applications that use large modular Javascript libraries.

Bundler

webpack is a JavaScript module bundler for web applications. Some of its main features and benefits are:

  • Bundling - webpack takes all Javascript, CSS, images, fonts and other files and dependencies and bundles them into one or more optimized bundles for production. This improves performance by reducing the number of requests.

  • Loaders - webpack's loaders allow you to integrate a variety of different languages and preprocessors into your build pipeline, such as JSX, TypeScript, SASS, Less etc.

  • Code splitting - webpack allows you to split your code into multiple bundles that can be loaded on demand or in parallel, improving page load time.

  • Minification and code optimization - webpack can automatically minify and optimize all the Javascript, CSS, HTML and images of your application for production through plugins.

  • Hot Module Replacement - HMR updates modules in the browser in real time without having to refresh the entire page, greatly improving the development experience.

  • Simplified environment and build - webpack takes care of the entire development and production build process, task configuration, environments and more.

Interpreters

A Javascript interpreter reads the Javascript source code line by line and executes it immediately, without a separate compilation step. As the interpreter walks through the code, it allocates memory for variables and functions and associates them with their respective scopes.

Hoisting refers to the behavior of the Javascript interpreter to move function, variable and class declarations to the top of their scopes. This happens because the interpreter makes two passes over the code:

On the first pass, the interpreter "hoists" all function and variable declarations to the top of their scopes. Functions are entirely moved, while only variable declarations are moved, not assignments.

On the second pass, the code is executed line by line. Since declarations have already been moved to the top


More posts

Cover Image for CI/CD - Lint - Checks

CI/CD - Lint - Checks

Comprehensive guide to continuous integration and continuous delivery. Explains key concepts, tools like GitHub Actions and Drone, benefits of linting, integrating ESLint and Prettier, and using Git hooks for automation.

Abílio Azevedo
Abílio Azevedo
Cover Image for ReWork

ReWork

Unconventional, straight-to-the-point advice on how to run a business. Instead of focusing on fast growth, Rework encourages entrepreneurs to start small, stay agile, and focus on what's essential.

Abílio Azevedo
Abílio Azevedo

NewsLetter

I will send the content posted here. No Spam =)