Skip to content

Javascript Fundamentals

100 companion flashcards · AI-assisted study content · Open the deck →

This deck is a friendly introduction to the building blocks of JavaScript, one of the most widely used programming languages on the web. Across the cards, you'll explore foundational concepts like what JavaScript is, how it fits into a web page, and the core syntax you'll use every day as a developer. Topics range from declaring variables and understanding primitive data types to working with operators, type coercion, and control flow statements like if-else and switch.

It's well suited for beginners who are just starting their journey into web development, whether you're learning on your own or taking your first programming course. If you have a little familiarity with HTML and want to add interactivity to your projects, these cards will give you a solid mental map of the language's essentials. More experienced developers can also use it as a quick refresher to brush up on terminology and core ideas.

To get the most out of your study sessions, try reviewing a small batch of cards each day rather than cramming everything at once. Spacing out your practice helps the concepts move from short-term memory into long-term recall. As you go through each card, take a moment to think about how the concept might show up in a real piece of code, since connecting definitions to small practical examples tends to make the ideas stick. Pairing these flashcards with a bit of hands-on coding in your browser's console will reinforce what you learn and make the material feel much more alive.

JavaScript Foundations

JavaScript is a high-level, interpreted programming language that runs in every modern browser and is the scripting language of the Web. It was created by Brendan Eich in 1995 while he was working at Netscape, and the language went through several early names—Mocha, then LiveScript—before it became JavaScript. JavaScript follows the ECMAScript specification, which defines the core features of the language. The latest stable version of that standard is ES2023, and JavaScript engines implement every ES feature plus additional browser-specific ones.

To run JavaScript in a web page, you embed it using the <script> tag. The script can be written inline directly between the opening and closing tags, or it can be loaded from an external file with an attribute like <script src="script.js"></script>. Placing the script just before the closing </body> tag is a common best practice because it lets the browser parse the HTML first, improving perceived performance. For day-to-day work, console.log() is the developer's main debugging tool: it prints messages to the browser console and accepts multiple arguments of any type, such as console.log('Hello', 42);.

Variables, Types, and Operators

JavaScript offers three keywords for declaring variables: var, which is function-scoped, and let and const, both of which are block-scoped and were introduced in ES6. const declares a binding that cannot be reassigned, while let allows reassignment within its block. The language has seven primitive data types: string, number, bigint, boolean, undefined, null, and symbol. Primitives are immutable values, and you can identify a value's type with the typeof operator, which returns a string such as 'string' when given 'hello'.

A frequent source of confusion is the difference between null and undefined. null represents an intentional absence of a value, whereas undefined means a variable has been declared but not yet assigned. Interestingly, typeof null returns 'object', a long-standing historical bug in the language. JavaScript also performs automatic type coercion, converting values between types in operations such as '5' + 1, which yields the string '51'. To avoid surprises from coercion, developers are encouraged to use === for strict equality instead of ==, because === compares both value and type without converting them.

JavaScript provides the usual arithmetic operators—+, -, *, /, % (modulus), and ** (exponentiation)—all of which work on numbers. Comparison operators such as ==, ===, !=, !==, and the relational operators all return a boolean (true or false). Logical operators && (AND), || (OR), and ! (NOT) perform boolean logic and enable short-circuit evaluation, which is the basis for idiomatic patterns such as value && doSomething().

Control Flow and Functions

Programs make decisions with if, else if, and else blocks. The condition in an if statement is evaluated for truthiness, and JavaScript will coerce any non-boolean value into a boolean in that context. When many branches depend on the same expression, a switch statement provides a cleaner alternative: it evaluates the expression once and dispatches to the matching case, with break statements used to stop execution from falling through, and an optional default branch for any unmatched value.

Loops repeat actions. A for loop uses the form for (init; condition; increment) { ... }, where the initializer runs once, the condition is checked at the start of every iteration, and the increment runs at the end. The while loop also checks its condition before each iteration, but the do-while loop flips that order: it executes the body first and then checks the condition, guaranteeing at least one iteration. These looping constructs let you traverse arrays, repeat calculations, or process user input until a stop condition is reached.

Functions are reusable blocks of behavior. They can be declared with function name(parameters) { ... }, in which case the declaration is hoisted so it can be called from earlier in the file, or assigned as expressions such as const func = function() {}, which are not hoisted. Parameters are the named variables in the function definition, while arguments are the actual values passed when calling the function; the legacy arguments object exposes every passed value. The return statement exits the function and sends a value back to the caller, and a function with no return implicitly returns undefined. Strict mode, enabled by placing the string literal 'use strict'; at the top of a script or function, enforces safer rules such as disallowing undeclared variables and duplicate parameter names.

Data Structures and ES6 Syntax

JavaScript's two main collection types are arrays and objects. Arrays are created with square brackets, for example const arr = [1, 'a', true]; or with the new Array() constructor, and they are zero-indexed collections that can hold values of any type. The mutating array methods follow a consistent pattern: push() adds to the end, pop() removes from the end, shift() removes from the start, and unshift() adds to the start. Objects are created with the object-literal syntax { key: value }, as in const obj = {name: 'JS'};, where keys are strings (or symbols) and values can be anything.

Object properties can be read or set with either dot notation, obj.prop, or bracket notation, obj['prop']. Bracket notation is required when the key is dynamic or stored in a variable. ES6 added a great deal of expressive syntax on top of these collections. Template literals, written with backticks, let you embed expressions directly into strings: `Hello ${name}` interpolates the value of name, and template literals also support multi-line strings and tagged templates. Destructuring assignment extracts values from arrays or objects into variables in a single step, such as const [a, b] = [1, 2]; or const {x} = {x: 10};.

The spread operator (...) expands an iterable into individual elements, which is handy for shallow copying arrays with const newArr = [...oldArr, 1]; or for passing many arguments to a function such as Math.max(...nums). The closely related rest parameter syntax also uses ... but in the opposite direction: in a function definition, function sum(...nums) {} collects all remaining arguments into a real array. Rest parameters must come last. Default parameters let you supply fallback values that are evaluated only when the argument is undefined, for example function greet(name = 'World') {}. Together, these features reduce boilerplate and make data manipulation concise.

Scope, Hoisting, and Closures

Scope is the region of code where a variable is accessible. The outermost region is the global scope, available everywhere in a script. Variables declared with var are function-scoped, meaning they are visible throughout the function in which they are declared regardless of block boundaries, while let and const are block-scoped, limited to the block such as a {}, if, or for body in which they appear. Hoisting describes JavaScript's behavior of moving declarations to the top of their scope before code runs. var declarations are hoisted and initialized to undefined, so a variable can be referenced before its declaration line without throwing. let and const are also hoisted but live in the Temporal Dead Zone until their declaration line, which throws if you try to read them earlier.

A closure is a function together with the variables of its outer scope that the function continues to reference, even after that outer function has finished executing. Closures are the basis for data privacy and the module pattern, because an inner function can capture local state that no one else can see. Closures only work because of lexical scope, which is the rule that a function's available variables are determined by where it was written in the source code, not by where it is eventually called. Confusing lexical scope with dynamic scope—for example, assuming a function sees the variables of wherever it is called rather than wherever it is defined—is a common pitfall, especially for developers coming from class-based languages.

Closures and scope rules combine to enable several useful patterns. An IIFE (Immediately Invoked Function Expression), written (function() { ... })();, runs once on definition and creates a private scope that prevents variables from leaking into the global namespace. JavaScript modules extend this idea across files: a module can export values, and another file can import them, organizing code and avoiding global namespace pollution. Each of these techniques—closures, IIFEs, modules—begins with the same principle that scope is determined by the lexical structure of the source.

Objects, Classes, and Built-in APIs

Even though ES6 introduced the class keyword, JavaScript's object system is built on prototypes. The prototype chain is how JavaScript looks up a property: if it is not found on an object itself, the engine looks on the object's prototype, then the prototype's prototype, and so on. This chain is what powers inheritance and is the reason methods can be shared across many objects. A common mistake is to confuse JavaScript prototypes with classical classes in languages like Java or C#—they look similar but differ in how inheritance actually works under the hood.

The ES6 class syntax is essentially syntactic sugar on top of prototypes. A class is defined with class MyClass { constructor(props) { this.props = props; } method() {} }. Class inheritance uses extends to create a child class and super() inside the child's constructor to call the parent constructor, ensuring the parent's initialization runs first. The this keyword refers to the execution context object: it points to window in the global scope, to the calling object inside regular methods, and is inherited from the surrounding lexical scope inside arrow functions.

JavaScript ships with many built-in objects for everyday tasks. The Math object offers utilities such as Math.random(), Math.floor(), and constants like Math.PI, with no constructor of its own. The Date object represents a moment in time and is created with new Date() for the current instant or new Date('2023-01-01') for a specific one; methods like getFullYear() and setHours() read and update parts of the date. JSON (JavaScript Object Notation) is a plain-text format for data interchange, written like {"name": "JS"}, and is converted to and from JavaScript objects with JSON.parse() and JSON.stringify(). Array methods such as map(), filter(), and reduce() provide functional-style transformations: map() builds a new array by applying a function to each element, filter() keeps only elements that pass a boolean test, and reduce() folds an array down to a single accumulated value.

Asynchronous JavaScript

Long-running operations like network requests must run asynchronously so the browser stays responsive. JavaScript represents these operations with Promises, which are objects that describe an eventual completion or failure. A promise is in one of three states—pending, fulfilled, or rejected—and you attach handlers with .then() for success and .catch() for errors. Promise chaining sequences asynchronous steps by returning a value or another promise from each .then() callback, which keeps async workflows flat rather than deeply nested. A common mistake when chaining is forgetting to return a promise, which makes the next step run before the previous one has actually settled.

The async and await keywords build on top of promises and make asynchronous code look synchronous. An async function always returns a promise, and await pauses execution inside the function until the awaited promise settles, as in const data = await fetch(url);. Errors must be handled with try/catch around await calls, or by chaining .catch() on the promise. A frequent bug in async error handling is wrapping only the synchronous parts of a function and missing rejected promises, which can silently break user flows. Asking the questions—"what problem is this solving?", "what trade-off does it create?", and "how will I know it worked?"—is a useful habit before introducing async logic in any real project.

Under the hood, the JavaScript engine maintains a call stack, a LIFO record of which functions are currently running, and combines it with the event loop to schedule asynchronous tasks such as setTimeout callbacks. Within that loop, microtasks—including promise callbacks—run with higher priority than regular macrotasks, executing after the current synchronous stack finishes but before the next macrotask. Understanding this distinction helps explain why a promise's .then() handler fires sooner than a setTimeout(fn, 0), and it is the foundation for predicting the order of asynchronous operations in modern JavaScript.

Browser Interaction and Best Practices

JavaScript's classic role is to manipulate the Document Object Model (DOM). You can select a single element with document.getElementById('id') or document.querySelector('.class'), and you can select many with document.querySelectorAll(), which returns a NodeList. Once you have an element, you can attach an event listener such as element.addEventListener('click', handler) and later remove it with removeEventListener(). The handler receives an event object e with details like e.target and helpers such as e.preventDefault() to cancel the browser's default behavior.

Instead of attaching listeners to many child elements, event delegation attaches a single listener to a parent and uses the fact that events bubble up through the DOM tree. This pattern improves performance, especially with hundreds of nodes, and works well for dynamically added elements. A common mistake is to add a separate listener to each child when one parent listener would handle them all. As with other techniques, trying it on a small realistic example and articulating the decision out loud—"what problem is this solving?", "what trade-off does it create?", and "how will I know it worked?"—is the best way to internalize the pattern before applying it broadly.

Modern JavaScript includes several conveniences and patterns worth knowing. Optional chaining (?.) safely reads nested properties or calls methods without throwing when an intermediate value is null or undefined. The nullish coalescing operator (??) returns its right-hand side only when the left side is null or undefined, unlike ||, which also treats values like 0, '', and false as falsy fallbacks. A pure function always returns the same output for the same input and avoids side effects such as mutating external state or making network calls, which makes programs easier to test and reason about. Debouncing delays a function call until activity stops, commonly used so that a search input triggers an API call only after the user pauses typing. Throttling ensures a function runs at most once per defined interval, ideal for handlers such as scroll, resize, and mousemove. Finally, embracing immutability—creating new arrays and objects rather than mutating existing ones—makes state changes predictable in UI frameworks and reducers; the common mistake is to mutate arrays or objects directly and expect change detection to work anyway. Together, these tools compose into idiomatic, robust JavaScript.

Frequently asked questions

What is JavaScript?

JavaScript is a high-level, interpreted programming language primarily used for web development to make web pages interactive. It is the scripting language of the Web, supported by all modern browsers, and follows the ECMAScript specification.

What is type coercion in JavaScript?

Type coercion is JavaScript's automatic conversion of values between types, like '5' + 1 becoming '51'. Use === for strict equality to avoid it.

What are function parameters and arguments?

Parameters are variables in the function definition; arguments are values passed when calling. Functions can access arguments object for all passed values.

What does the 'this' keyword refer to?

'this' refers to the execution context object. In global scope it's window (browser); in methods it's the object; arrow functions inherit from parent.

What is class inheritance?

Use extends and super(), e.g., class Child extends Parent { constructor() { super(); } }. Calls parent constructor.

What are Promises?

Promises represent eventual completion/failure of async operations, with .then(), .catch(). States: pending, fulfilled, rejected.

What is throttling?

Throttling ensures a function runs at most once in a defined interval, useful for scroll, resize, and mouse-move handlers.

Why does event delegation matter in JavaScript fundamentals?

It improves performance and works well for dynamically added elements.

What question should you ask when using Promise chaining?

Ask: what problem is this solving, what trade-off does it create, and how will I know it worked?

Why does immutability matter in JavaScript fundamentals?

It makes state changes easier to reason about in UI frameworks and reducers.

Drill this topic

100 flashcards on Javascript Fundamentals — free, no signup needed to start.

Study Javascript Fundamentals flashcards

LearnWiki pages are generated with AI assistance from LearnCoachAssist's reviewed study catalog and may contain errors — verify anything critical against your course materials.