JavaScript: the Web Programming Language

[...]

JavaScript Data Types

Primitive and Complex Data Types

In JavaScript, a primitive value is a single value with no properties or methods.

JavaScript has seven primitive data types:

  • string
  • number
  • boolean
  • bigint
  • symbol
  • null
  • undefined

JavaScript has one complex data type: object. All other complex types like arrays, functions, sets, and maps are just different types of objects.

The typeof operator returns only two types:

  • object
  • function

Example

typeof {name:'John'}   // Returns object
typeof [1,2,3,4]       // Returns object
typeof new Map()       // Returns object
typeof new Set()       // Returns object

typeof function (){}   // Returns function

How to Recognize an Array

How to know if a variable is an array?

ECMAScript 5 (2009) defined a new method for this: Array.isArray()

Example

// Create an Array
const fruits = ["apples", "bananas", "oranges"];
Array.isArray(fruits);

JavaScript Types null and undefined

The typeof of a variable with no value is undefined. The value is also undefined.

The typeof of a non-existing property is undefined.

An undefined yilds equal to another undefined:

undefined === undefined    // true

In JavaScript null is nothing. It is supposed to be something that doesn't exist. Unfortunately, in JavaScript, the data type of null is an object.

You can empty an object by setting it to null.


undefined and null are equal in value but different in type:

typeof undefined      // undefined
typeof null           // object
null === undefined    // false
null == undefined     // true

JavaScript typeof Operator*

The typeof operator returns the type of a variable or an expression.

Symbols

Symbols are values created with the Symbol(STRING) function. Unlike strings, newly created symbols are unique—you cannot create the same symbol twice.

What's the use of symbols? It is also possible to use symbols as property names.

Here's an example:

let sym = Symbol("name");
console.log(sym == Symbol("name"));
// → false
Rabbit.prototype[sym] = 55;
console.log(killerRabbit[sym]);
// → 55

The string you pass to Symbol(...) is included when you convert it to a string and can make it easier to recognize a symbol when, for example, showing it in the console. But it has no meaning beyond that—multiple symbols may have the same name.

Being both unique and usable as property names makes symbols suitable for defining interfaces that can peacefully live alongside other properties, no matter what their names are.

const length = Symbol("length");
Array.prototype[length] = 0;

console.log([1, 2].length);
// → 2
console.log([1, 2][length]);
// → 0

It is possible to include symbol properties in object expressions and classes by using square brackets around the property name. That causes the expression between the brackets to be evaluated to produce the property name, analogous to the square bracket property access notation.

let myTrip = {
  length: 2,
  0: "Lankwitz",
  1: "Babelsberg",
  [length]: 21500
};

console.log(myTrip[length], myTrip.length);
// → 21500 2

          

        

JavaScript Iterables

An Iterable is an Iterable Object.

An iterable can be iterated over with for... of loops

The for... of Loop

The JavaScript for... of statement loops through the elements of an iterable object.

for (variable of iterable) {
  // code block to be executed
}

Iterating means looping over a sequence of elements. Here are some easy examples:

  • iterating over a String
  • iterating over an Array
  • iterating over a Set
  • iterating over a Map

Iterators

The iterator protocol defines how to produce a sequence of values from an object. An object becomes an iterator when it implements a next() method. The next() method must return an object with two properties:

  • value: the next value
  • done: true or false

Implementing your own iterable object

This iterable returns never ending number: 10,20,30,40,.... when the next() method is called:

// Home Made Iterable
function myNumbers() {
  let n = 0;
  return {
    next: function() {
      n += 10;
      return {value:n, done:false};
    }
  };
}

// Run the Iterable
const n = myNumbers();
n.next(); // Returns 10
n.next(); // Returns 20
n.next(); // Returns 30

A fully-compliant iterable:

// Create an Object
myNumbers = {};

// Make it Iterable
myNumbers[Symbol.iterator] = function() {
  let n = 0;
  done = false;
  return {
    next() {
      n += 10;
      if (n == 100) {done = true}
      return {value:n, done:done};
    }
  };
}

Now you can use for..of

for (const num of myNumbers) {
  // Any Code Here
}

The Symbol.iterator method is called automatically by for..of. But we can also do it "manually":

let iterator = myNumbers[Symbol.iterator]();

while (true) {
  const result = iterator.next();
  if (result.done) break;
  // Any Code Here
}

Helper Functions

(From https://www.w3schools.com/js/js_iterators.asp)

JavaScript 2025 (ECMAScript 2025) officially approved a set of new Iterator Helper methods that significantly enhance the functionality of iterators in JavaScript. The methods provide a more functional and efficient way to work with iterable objects, including generators, by allowing direct manipulation and transformation without first converting them to arrays:

Function Description
drop() Returns an iterator that skips a specified number of elements before yielding the rest
every() Returns true if all elements satisfy a test function
filter() Returns an iterator containing elements that satisfy a filter function
find() Returns the first element that satisfies a test function
flatMap() Returns an iterator by mapping each element and then flattening the results
forEach() Executes a function once for each element in the iterator.
from() creates an iterator object from an iterable
map() Returns an iterator with all elements transformed by a map function
reduce() Applies a reducer function against each element to reduce it to a single value
some() Returns true if at least one element satisfy a test function
take() Returns an iterator that yields a specified number of elements

JavaScript Control Structures

The for KEY in OBJECT Loop

The JavaScript for KEY in OBJECT statement loops through the properties of an Object:

Syntax

for (key in object) {
  // code block to be executed
}

An example:

const person = {fname:"John", lname:"Doe", age:25};

let text = "";
for (let x in person) {
  text += person[x];
}

The JavaScript for in statement can also loop over the properties of an Array:

Syntax

for (VARIABLE in ARRAY) {
  //code
}

Example

const numbers = [45, 4, 9, 16, 25];

let txt = "";
for (let x in numbers) {
  txt += numbers[x];
}

The for of Loop

The JavaScript for of statement loops through the values of an iterable object. It lets you loop over iterable data structures such as Arrays, Strings, Maps, NodeLists, and more.

Syntax

for (variable of iterable) {
  // code block to be executed
}

An example of looping over an Array

const cars = ["BMW", "Volvo", "Mini"];

let text = "";
for (let x of cars) {
  text += x;
}

The while Loop

The while loop loops through a block of code as long as a specified condition is true.

Syntax

while (condition) {
  // code block to be executed
}

In the following example, the code in the loop will run, over and over again, as long as a variable (i) is less than 10:

while (i < 10) {
  text += "The number is " + i;
  i++;
}

The do while loop is a variant of the while loop. This loop will execute the code block once, before checking if the condition is true, then it will repeat the loop as long as the condition is true.

Syntax

do {
  // code block to be executed
}
while (condition);

The JavaScript switch Statement

Use the switch statement to select one of many code blocks to be executed.

Syntax

switch(expression) {
  case x:
    // code block
    break;
  case y:
    // code block
    break;
  default:
    // code block
}

Example. The getDay() method returns the weekday as a number between 0 and 6.

(Sunday=0, Monday=1, Tuesday=2 ..)

This example uses the weekday number to calculate the weekday name:

let day = "ANYDAY";

switch (new Date().getDay()) {
  case 0:
    day = "Sunday";
    break;
  case 1:
    day = "Monday";
    break;
  case 2:
     day = "Tuesday";
    break;
  case 3:
    day = "Wednesday";
    break;
  case 4:
    day = "Thursday";
    break;
  case 5:
    day = "Friday";
    break;
  case 6:
    day = "Saturday";
}

JavaScript Functions

Local Variables

A local variable is a private variable defined inside a function. A function can access all variables in the local scope.

In the following example, a is a local variable defined inside the function:

function myFunction() {
  let a = 4;
  return a * a;
}

A global variable is a public variable defined outside a function. A function can access all variables in the global scope.

Nested Functions

All functions have access to the global scope. In fact, in JavaScript, all functions have access to the scope above them.

Besides, JavaScript supports nested functions. Nested functions have access to the scope above them, even if it is local.

Example The inner function plus() has access to the counter variable in the parent function:

function add() {
  let counter = 0;
  function plus() {counter += 1;}
  plus();
  return counter;
}

JavaScript Closures

A closure is a function that has access to the parent scope, after the parent function has closed.

Example

function myCounter() {
  let counter = 0;
  return function() {
    counter++;
    return counter;
  };
}
const add = myCounter();
add();
add();
add();

// the counter is now 3

Explanation

The variable add is assigned to the return value of a function.

The function only runs once. It sets the counter to zero (0), and returns a function expression.

This way add becomes a function. The wonderful part is that it can access the counter in its parent scope. This is called a closure. It makes it possible for a function to have private variables.

The counter is protected by the scope of the myCounter function, and can only be changed using the add function.


Closures has historically been used to:

  • Create private variables
  • Preserve state between function calls
  • Simulate block-scoping before let and const existed
  • Implement certain design patterns like currying and memoization

Arrow Function

Arrow functions were introduced in ES6. They allow us to write shorter function syntax:

let myFunction = (a, b) => a * b;

Before arrow functions you could only code:

hello = function() {
  return "Hello World!";
}

Before arrow functions you can write:

hello = () => {
  return "Hello World!";
}

It gets shorter! If the function has only one statement, and the statement returns a value, you can remove the brackets and the return keyword. (Arrow functions return a value by default.)

hello = () => "Hello World!"; 

When Arrow Function take Parameters, you enclose them in round brackets. In fact, if you have only one parameter, you can skip the parentheses as well

hello = (val) => "Hello " + val;
hello = val => "Hello " + val;

Function Borrowing

With the bind() method, an object can borrow a method from another object.

The example below creates 2 objects (person and member). The member object borrows the fullname method from the person object:

const person = {
  firstName:"John",
  lastName: "Doe",
  fullName: function () {
    return this.firstName + " " + this.lastName;
  }
}

const member = {
  firstName:"Hege",
  lastName: "Nilsen",
}

let fullName = person.fullName.bind(member);

Sometimes the bind() method has to be used to prevent losing object variable this.

In the following example, the person object has a display method. In the display method, this refers to the person object:

const person = {
  firstName:"John",
  lastName: "Doe",
  display: function () {
    let x = document.getElementById("demo");
    x.innerHTML = this.firstName + " " + this.lastName;
  }
}

person.display();

When a function is used as a callback, object variable this is lost.

This example will try to display the person name after 3 seconds, but it will display undefined instead:

const person = {
  firstName:"John",
  lastName: "Doe",
  display: function () {
    let x = document.getElementById("demo");
    x.innerHTML = this.firstName + " " + this.lastName;
  }
}

setTimeout(person.display, 3000);

The bind() method solves this problem.

In the following example, the bind() method is used to bind person.display to person. This example will display the person name after 3 seconds:

const person = {
  firstName:"John",
  lastName: "Doe",
  display: function () {
    let x = document.getElementById("demo");
    x.innerHTML = this.firstName + " " + this.lastName;
  }
}

let display = person.display.bind(person);
setTimeout(display, 3000);


        

JavaScript Operators

JavaScript Spread (...) Operator

The spread (...) syntax allows an iterable, such as an array or string, to be expanded in places where zero or more arguments (for function calls) or elements (for array literals) are expected. In an object literal, the spread syntax enumerates the properties of an object and adds the key-value pairs to the object being created.

Spread syntax looks exactly like rest syntax. Yet, in a way, spread syntax is the reverse of rest syntax. Spread syntax expands an array into its elements, while rest syntax collects multiple elements and condenses them into a single element. (See rest parameters and rest property.)

An example expanding the arguments into a function:

function sum(x, y, z) {
  return x + y + z;
}

const numbers = [1, 2, 3];

console.log(sum(...numbers));
// Expected output: 6

console.log(sum.apply(null, numbers));
// Expected output: 6

Spread syntax can be used when all elements from an object or array need to be included in a new array or object, or should be applied one-by-one in a function call's arguments list. There are three distinct places that accept the spread syntax:

  • Function arguments list: myFunction(a, ...iterableObj, b)
  • Array literals: [1, ...iterableObj, '4', 'five', 6]
  • Object literals: { ...obj, key: 'value' }

Although the syntax looks the same, they come with slightly different semantics.

Only iterable values, like Array and String, can be spread in array literals and argument lists. Many objects are not iterable, including all plain objects that lack a Symbol.iterator method:

const obj = { key1: "value1" };
const array = [...obj]; // TypeError: obj is not iterable

You can use spread syntax to make a shallow copy of an array. Each array element retains its identity without getting copied.

const arr = [1, 2, 3];
const arr2 = [...arr]; // like arr.slice()

arr2.push(4);
// arr2 becomes [1, 2, 3, 4]
// arr remains unaffected

Copying and merging objects

You can use spread syntax to merge multiple objects into one new object.

const obj1 = { foo: "bar", x: 42 };
const obj2 = { bar: "baz", y: 13 };

const mergedObj = { ...obj1, ...obj2 };
// { foo: "bar", x: 42, bar: "baz", y: 13 }

A single spread creates a shallow copy of the original object (but without non-enumerable properties and without copying the prototype), similar to copying an array.

const clonedObj = { ...obj1 };
// { foo: "bar", x: 42 }

When one object is spread into another object, or when multiple objects are spread into one object, and properties with identical names are encountered, the property takes the last value assigned while remaining in the position it was originally set.

const obj1 = { foo: "bar", x: 42 };
const obj2 = { foo: "baz", y: 13 };

const mergedObj = { x: 41, ...obj1, ...obj2, y: 9 }; // { x: 42, foo: "baz", y: 9 }

You can make an element present or absent in an object literal, depending on a condition, using a conditional operator (?:).

const isSummer = false;
const fruits = {
  apple: 10,
  banana: 5,
  ...(isSummer ? { watermelon: 30 } : {}),
};
// { apple: 10, banana: 5 }

The case where the condition is false is an empty object, so that nothing gets spread into the final object. Note that this is different from the following:

const fruits = {
  apple: 10,
  banana: 5,
  watermelon: isSummer ? 30 : undefined,
};
// { apple: 10, banana: 5, watermelon: undefined }

In this case, the watermelon property is always present and will be visited by methods such as Object.keys().

Because primitives can be spread into objects as well, and from the observation that all falsy values do not have enumerable properties, you can simply use a logical AND operator:

const isSummer = false;
const fruits = {
  apple: 10,
  banana: 5,
  ...(isSummer && { watermelon: 30 }),
};

In this case, if isSummer is any falsy value, no property will be created on the fruits object.

JavaScript Rest (...) Operator*

Comparison and Logical Operators

Comparison and Logical operators are used to test for true or false.

Comparison operators ==, < and so on are very much like those found in C/C++. Additionally, strict comparison (===) returns true if both operands are the same type.

Logical operators &&, || and !, as well as conditional operator (?:) are also the same as in C/C++.


When comparing a string with a number, JavaScript will convert the string to a number when doing the comparison. An empty string converts to 0. A non-numeric string converts to NaN which is always false. Thus 3 < "4" yields true.


          


        

The Nullish Coalescing Operator (??)

The ?? operator returns the first argument if it is not nullish (null or undefined). Otherwise it returns the second argument. Example:

let name = null;
let text = "missing";
let result = name ?? text;

The Optional Chaining Operator (?.)

The ?. operator returns undefined if an object is undefined or null (instead of throwing an error). Example:

// Create an object:
const car = {type:"Fiat", model:"500", color:"white"};
// Ask for car name:
document.getElementById("demo").innerHTML = car?.name;

JavaScript Regular Expressions

Regular expressions are patterns used to match character combinations in strings. In JavaScript, regular expressions are also objects. These patterns are used with the exec() and test() methods of RegExp, and with the match(), matchAll(), replace(), replaceAll(), search(), and split() methods of String.


To learn more, click to read a thorough discussion of JavaScript Regular Expressions

Creating regular expressions

A regular expression is typically created as a literal by enclosing a pattern in forward slashes (/):

const regex1 = /ab+c/g;

Regular expressions can also be created with the RegExp() constructor:

const regex2 = new RegExp("ab+c", "g");

They have no runtime differences, although they may have implications on performance, static analyzability, and authoring ergonomic issues with escaping characters.

Reading and Writing to a File with JavaScript

Including a (Local) File

A simple way to include a file containing valid JavaScript, just apply the following syntax:

<script type="text/javascript" src="./MYFILE.js"></script>

Writing to a (Local) File through a Blob

(Exemplified in template.js.write.blob.html)

The Blob object is a built-in JavaScript feature that allows you to create file-like objects of immutable raw data. Using a Blob, you can easily generate text files directly in the browser. Here's how to use it:

  • Create a Blob object containing the data you want to write to a text file.
  • Use the URL.createObjectURL() method to create a downloadable link.
  • Trigger the file download automatically when the user clicks a button.

The (relevant) code:

    <div>
      <textarea id="textData" rows="6" cols="50" placeholder="Enter text to save"></textarea><br/><br/>
      <textarea id="fileName" rows="1" cols="50" placeholder="FILENAME.txt"></textarea><br/><br/>
      <button onclick="downloadTextFile()">Download Text File</button>
    </div>
    <script>
      function downloadTextFile() {
        var text = document.getElementById("textData").value;
        var filename = document.getElementById("fileName").value;
        var blob = new Blob([text], { type: 'text/plain' });
        var link = document.createElement('a');
        link.href = URL.createObjectURL(blob);
        link.download = filename;
        link.click();
      }
    </script>
          

(You may of course change the identifiers, forego embedding your textarea and button in a div element, etc.)

File I/O in Node.js File through the System Module (fs)

(From https://www.w3schools.com/nodejs/nodejs_filesystem.asp)

The Node.js File System module (fs) provides a comprehensive set of methods for working with the file system on your computer. It allows you to perform file I/O operations in both synchronous and asynchronous ways.

Note: The File System module is a core Node.js module, so no installation is required.

In depth discussion to be found at js.nodejs.html#filesystem.


Some examples/demos: