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
andconst
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: