High-Level JavaScript: Types, Values, and Variables
This item is part of the 2nd Hell Week in the Atlas of the Web program.
Before the types
(source: D. Flanagan, JavaScript. The Definitive Guide)
JavaScript is case-sensitive, meaning that keywords, variables, function names, and other identifiers must have consistent capitalization. For example, the while keyword must be written exactly as “while,” not “While” or “WHILE.” Similarly, online, Online, and ONLINE represent different variable names.
JavaScript supports two styles of comments. Any text between a // and the end of a line is treated as a comment and is ignored by JavaScript. Any text between the char‐ acters /* and */ is also treated as a comment; these comments may span multiple lines but may not be nested.
An identifier is simply a name. In JavaScript, identifiers are used to name constants, variables, properties, functions, and classes and to provide labels for certain loops in JavaScript code. A JavaScript identifier must begin with a letter, an underscore (_), or a dollar sign ($). Subsequent characters can be letters, digits, underscores, or dollar signs. (Digits are not allowed as the first character so that JavaScript can easily distin‐ guish identifiers from numbers.)
Reserved keywords
as, async, await, break, case, catch, class, const, continue, debugger,
default, delete, do, else, enum, export, extends, false, finally, for,
function, if, implements, import, in, instanceof, interface, let, new,
null, package, private, protected, public, return, super, switch, static,
this, throw, true, try, typeof, var, void, while, with, yield
copy
Future reserved keywords
enum, implements, interface, package, private, protected, public
copy
These words have special meanings in certain contexts but can be used as identifiers outside of those contexts:
get, set, from, of, target
copy
These words should be avoided as identifiers in certain cases due to their special roles in JavaScript:
arguments, eval
copy
Unicode
JavaScript programs are written using the Unicode character set, and you can use any Unicode characters in strings and comments. For portability and ease of editing, it is common to use only ASCII letters and digits in identifiers. But this is a programming convention only, and the language allows Unicode letters, digits, and ideographs (but not emojis) in identifiers. This means that programmers can use mathematical sym‐ bols and words from non-English languages as constants and variables:
const π = 3.14;
const sí = true;
copy
Optional semicolons
Note that JavaScript does not treat every line break as a semicolon: it usually treats line breaks as semicolons only if it can’t parse the code without adding an implicit semicolon. More formally (and with three exceptions described a bit later), JavaScript treats a line break as a semicolon if the next nonspace character cannot be interpreted as a continuation of the current statement. Consider the following code:
let a
a
=
3
console.log(a)
copy
JavaScript interprets this code like this:
let a; a = 3; console.log(a);
copy
JavaScript does treat the first line break as a semicolon because it cannot parse the code let a a
without a semicolon. The second a
could stand alone as the statement a;
, but JavaScript does not treat the second line break as a semicolon because it can continue parsing the longer statement a = 3;
.
These statement termination rules lead to some surprising cases. This code looks like two separate statements separated with a newline:
lety=x+f
(a+b).toString()
copy
But the parentheses on the second line of code can be interpreted as a function invo‐ cation of f from the first line, and JavaScript interprets the code like this:
let y = x + f(a+b).toString();
copy
1. When Two Statements Are on Separate Lines
If two statements are written on separate lines, JavaScript will insert a semicolon between them:
let a = 5
let b = 10
copy
JavaScript interprets this as:
let a = 5;
let b = 10;
copy
2. After a Return, Break, Continue, or Throw Statement
If a line break follows any of these keywords (return, break, continue, throw), JavaScript will automatically insert a semicolon. However, if an expression follows the keyword on the same line, no semicolon is added.
return
true;
copy
This is interpreted as:
return;
true;
copy
However, if it’s written like this:
return true;
copy
No semicolon is added because the true expression is on the same line.
3. When Closing a Block (After })
JavaScript automatically inserts semicolons after block-closing braces (}), such as the end of a function or control structure.
if (true) {
console.log("Hello")
}
copy
This is interpreted as:
if (true) {
console.log("Hello");
}
copy
4. At the End of a Program
JavaScript will automatically insert a semicolon at the end of a program if the last statement does not have one.
console.log("End of program")
copy
This will be interpreted as:
console.log("End of program");
copy
5. When a Line Break Cannot Be Interpreted as a Continuation
If JavaScript cannot interpret a line break as a continuation of the current statement, it inserts a semicolon. For example:
let a
a = 5
copy
This is interpreted as:
let a;
a = 5;
copy
6. When a Line Starts with (, [, /, +, or -
If a statement begins with any of these characters, JavaScript may interpret them as continuations of the previous statement unless a semicolon is inserted. Therefore, ASI will often insert semicolons to prevent incorrect interpretation:
let x = 3
;(x + 1).toString(); // Defensive semicolon
copy
In this case, the semicolon before the parentheses prevents JavaScript from thinking the two lines are part of the same statement.
Exceptions to Automatic Semicolon Insertion
There are a few exceptions where ASI doesn’t behave as expected:
Statements involving the return, break, or continue keywords: If you place an expression on the next line after these keywords, JavaScript assumes a semicolon after the keyword, potentially causing unintended behavior.
Example:
return
5;
copy
JavaScript interprets this as:
return;
5; // This value is unreachable.
copy
In summary, JavaScript automatically inserts semicolons in several cases, but it’s good practice to manually add semicolons in critical areas (especially after return, break, continue, or throw), as relying on ASI can sometimes lead to confusing bugs.
Types
JavaScript has several data types that are divided into two main categories: primitive types and object types.
Primitive Types
Primitive types are the most basic types in JavaScript. They are immutable (i.e., they cannot be changed) and are passed by value.
Number
Represents both integer and floating-point numbers.
let age = 30; // Integer
let price = 19.99; // Floating point
copy
let x = 10;
let y = 20;
// Arithmetic operations
let sum = x + y; // 30
let difference = y - x; // 10
let product = x * y; // 200
let quotient = y / x; // 2
let remainder = y % 3; // 2
let power = x ** 2; // 100
// Comparison
console.log(x > y); // false
console.log(x === 10); // true
copy
BigInt
Represents integers of arbitrary precision, allowing you to work with numbers larger than the safe limit for Number type.
let bigInt = 1234567890123456789012345678901234567890n;
copy
Operations are same as numbers, but it uses BigInt literals (n
suffix).
let bigInt = 9007199254740991n;
let bigIntSum = bigInt + 1n; // 9007199254740992n
console.log(bigIntSum); // 9007199254740992n
copy
String
Represents a sequence of characters (text).
let name = "Alice";
copy
let greeting = "Hello";
let name = "Alice";
// Concatenation
let fullGreeting = greeting + ", " + name + "!"; // "Hello, Alice!"
// Accessing characters
console.log(greeting[0]); // "H"
console.log(greeting.charAt(1)); // "e"
// String length and methods
console.log(greeting.length); // 5
console.log(greeting.toUpperCase()); // "HELLO"
copy
Boolean
Represents logical values: true or false.
let isLoggedIn = true;
copy
let isLoggedIn = true;
let isAdmin = false;
// Logical operations
console.log(isLoggedIn && isAdmin); // false
console.log(isLoggedIn || isAdmin); // true
console.log(!isAdmin); // true
// Conditional checks
if (isLoggedIn) {
console.log("Welcome!");
}
copy
Undefined
A variable that has been declared but has not been assigned a value.
let x;
console.log(x); // undefined
copy
Null
Represents the intentional absence of any object value.
let y = null;
copy
Symbol
Represents a unique and immutable value, often used as an object key.
let sym = Symbol("unique");
copy
let sym1 = Symbol("id");
let sym2 = Symbol("id");
console.log(sym1 === sym2); // false (unique)
let obj = {
[sym1]: "Alice"
};
console.log(obj[sym1]); // "Alice"
copy
Object Types
Objects are complex data structures in JavaScript that can store collections of data and more complex entities. Unlike primitives, objects are mutable and are passed by reference.
Object
Represents collections of key-value pairs.
let person = { name: "Alice", age: 30 };
copy
let person = { name: "Alice", age: 30 };
// Access properties
console.log(person.name); // "Alice"
console.log(person["age"]); // 30
// Add or update properties
person.job = "Engineer";
person.age = 31;
// Delete properties
delete person.job;
copy
Array
A special kind of object used for storing ordered collections.
let numbers = [1, 2, 3, 4];
copy
let numbers = [1, 2, 3, 4];
// Accessing elements
console.log(numbers[0]); // 1
// Array length
console.log(numbers.length); // 4
// Adding and removing elements
numbers.push(5); // Adds 5 at the end
numbers.pop(); // Removes the last element
copy
Function
Functions are objects in JavaScript that can be called or invoked to perform actions.
function greet() { return "Hello"; }
copy
function greet(name) {
return "Hello, " + name + "!";
}
console.log(greet("Alice")); // "Hello, Alice!"
copy
Date
Used to represent dates and times.
let today = new Date();
copy
let now = new Date();
// Getting date components
console.log(now.getFullYear()); // Current year
console.log(now.getMonth()); // Month (0-indexed)
// Creating a specific date
let birthday = new Date(1990, 6, 20); // July 20, 1990
copy
RegExp
Represents regular expressions, used for pattern matching in strings.
let pattern = /ab+c/;
copy
let pattern = /hello/i; // Case-insensitive match for "hello"
let str = "Hello world";
// Test if the string contains the pattern
console.log(pattern.test(str)); // true
copy
Map
A collection of keyed data items, where keys can be of any type.
let map = new Map();
copy
let map = new Map();
// Set key-value pairs
map.set("name", "Alice");
map.set(1, "one");
// Get values by key
console.log(map.get("name")); // "Alice"
console.log(map.has(1)); // true
// Delete a key-value pair
map.delete(1);
copy
Set
Represents a collection of unique values.
let set = new Set([1, 2, 3]);
copy
let set = new Set([1, 2, 3]);
// Add a value
set.add(4);
// Check if a value exists
console.log(set.has(2)); // true
// Delete a value
set.delete(3);
copy
WeakMap and WeakSet
A variation of Map where keys are objects and are weakly referenced, meaning the keys can be garbage collected if no other reference exists.
Similar to Set, but with weakly held object references, allowing garbage collection.
let weakMap = new WeakMap();
let obj = {};
weakMap.set(obj, "data");
console.log(weakMap.get(obj)); // "data"
copy
let weakSet = new WeakSet();
let obj = {};
weakSet.add(obj);
console.log(weakSet.has(obj)); // true
copy
Variables
Variable Declaration
JavaScript provides three ways to declare variables: var, let, and const. Each has different scoping and behavior.
var: Declares a function-scoped or globally-scoped variable, depending on where it is declared. Variables declared with var can be re-declared and updated.
var x = 10;
var x = 20; // Valid re-declaration
copy
let: Declares a block-scoped variable. Variables declared with let can be updated but not re-declared within the same scope.
let y = 30;
y = 40; // Valid update
let y = 50; // Error: y has already been declared in the same block
copy
const: Declares a block-scoped constant, meaning its value cannot be reassigned after declaration. However, const objects can still have their properties modified.
const z = 50;
// z = 60; // Error: Assignment to constant variable
const person = { name: "Alice" };
person.name = "Bob"; // Valid: properties of a const object can change
copy
Variable Scope
Variables in JavaScript have different types of scope, depending on how and where they are declared.
Global Scope: Variables declared outside any function or block are globally scoped and accessible throughout the entire program.
Function Scope: Variables declared with var inside a function are scoped to that function. They cannot be accessed outside the function.
Block Scope: Variables declared with let or const inside a block (like inside {} in loops or conditionals) are scoped to that block.
function myFunction() {
var x = 10; // Function-scoped
if (true) {
let y = 20; // Block-scoped
}
console.log(y); // Error: y is not defined (block scope)
}
copy
Hoisting
JavaScript variables declared with var are “hoisted” to the top of their scope (function or global scope). This means you can use them before they are declared, though they will be initialized as undefined.
Variables declared with let and const are hoisted as well but cannot be accessed before their declaration (this is called the temporal dead zone).
console.log(a); // undefined (due to var hoisting)
var a = 5;
//console.log(b); // Error: Cannot access 'b' before initialization
let b = 10;
copy
Variable Initialization
Variables declared with var are automatically initialized with undefined. Variables declared with let and const are not initialized until the point of declaration.
var x; // Automatically initialized with undefined
let y; // Not initialized until assigned
console.log(x); // undefined
console.log(y); // undefined (but no automatic initialization)
copy
Re-declaration and Re-assignment
var: Can be re-declared and re-assigned in the same scope.
let: Can be re-assigned but not re-declared in the same scope.
const: Cannot be re-assigned or re-declared. However, for objects, properties can be modified.
var x = 10;
var x = 20; // Re-declaration is allowed
let y = 10;
y = 20; // Re-assignment is allowed
// let y = 30; // Error: Re-declaration is not allowed
const z = 10;
// z = 20; // Error: Re-assignment is not allowed
copy
Global Variables
Variables declared without let, var, or const automatically become global variables, even if declared inside a function. This can lead to unintended behavior and is generally discouraged.
function myFunction() {
x = 10; // Global variable (bad practice)
}
myFunction();
console.log(x); // 10
copy
Shadowing
A variable declared within a certain scope can shadow a variable of the same name in an outer scope. The inner variable “hides” the outer variable within that specific scope.
let x = 10;
function myFunction() {
let x = 20; // Shadows the global x
console.log(x); // 20 (local variable)
}
myFunction();
console.log(x); // 10 (global variable)
copy
Closures
A closure occurs when an inner function “remembers” the variables from its outer function’s scope even after the outer function has returned. This allows inner functions to access variables from the outer function even when it is no longer active.
function outer() {
let counter = 0;
return function() {
counter++;
return counter;
};
}
let increment = outer();
console.log(increment()); // 1
console.log(increment()); // 2
copy
Destructuring Assignment
JavaScript provides a way to unpack values from arrays or properties from objects into distinct variables using destructuring assignment.
Array destructuring:
let [a, b] = [1, 2];
console.log(a); // 1
console.log(b); // 2
copy
Object destructuring:
let { name, age } = { name: "Alice", age: 25 };
console.log(name); // "Alice"
console.log(age); // 25
copy
Variable Best Practices
Use let and const: Avoid using var as it has function scope and can lead to bugs related to hoisting.
Use const by default: Use const unless you know that the variable will need to be re-assigned.
Keep variables within the smallest scope possible: Declare variables inside the block or function where they are used.
These are the core concepts related to variables in JavaScript, each contributing to how data is stored, accessed, and manipulated in JavaScript programs. Understanding these will help you write more efficient and error-free code.