JavaScript is one of the most flexible tools for web design—just take a look at our roundup of top JavaScript examples. But even with such a powerful tool, there’s always more to learn.
Below are 12 JavaScript questions (and answers) that have puzzled many developers—even seasoned pros. (If you’re looking to level up further, check out our CSS and JavaScript tutorials.) And if you want to keep site building simple, a website builder might be your best bet.

On this page, we’ll break down common questions—including ones you’ll often see in job interviews. On the next page, we’ll dive deeper into two trickier topics: using JavaScript to boost site performance (pro tip: great web hosting also keeps your site running smoothly) and future-proofing your JavaScript code.
01. What is prototypical inheritance, and how useful is it?
Nearly every element in JavaScript is an object—and every object has a prototype, a blueprint from which it inherits both values and behaviors. If an object doesn’t have a property you’re trying to access, JavaScript will look for that property in its prototype instead. It keeps traversing up the prototype chain until it finds a match… or returns an error.

This is incredibly useful when you’re creating multiple objects that share the same values or operations. By storing those shared traits in a prototype, you only need one copy of them—making your project far more memory-efficient.
You can add prototypes to objects when you create them using Object.create(), or later with Object.setPrototypeOf(). ES2015 introduced the class keyword; when paired with extends, the new class uses the specified value as its prototype.
02. How can JavaScript improve web accessibility?
Most modern web accessibility tools (like screen readers) can handle JavaScript and dynamic content these days—but only if you use JavaScript as an enhancement, not a requirement for basic functionality.
One common way to help users is through thoughtful focus management. For example, a calendar widget should let users cycle through days with arrow keys, and skip weeks with up/down keys. This just means listening for keyboard events when the user’s focus is on that calendar.
Another key step: any important updates driven by JavaScript (like form feedback) should be announced to screen readers. The easiest way to do this is by marking a container as a live region—a special HTML attribute that tells assistive tools to monitor for changes.
03. What is event bubbling, and how does it differ from event capturing?
Event capturing and bubbling are both part of event propagation—the process browsers use to respond to user actions (like clicks or taps) on a page. Older browsers only supported one or the other, but today all major browsers use both.

- Capturing phase: This happens first, right when an event is triggered. It starts at the topmost level (either document or window, depending on the event) and works its way down through the <html> tag and nested elements until it reaches the element where the event originated.
- Bubbling phase: Next, the process reverses. The event starts at the element that triggered it and “bubbles up” through parent elements until it reaches the topmost <html> tag. When you add event listeners in JavaScript, bubbling is the default behavior.
04. How does event delegation improve code for sites with lots of interactive elements?
Many websites have dynamic content that updates constantly—and if that content needs to be interactive, you’d normally add event listeners to each element. But if you have dozens (or hundreds) of interactive elements, this clutters your code and forces the browser to track far more data than necessary.

Event delegation fixes this by leveraging event bubbling. Instead of adding a listener to every child element, you add one listener to a parent element. When an event occurs on a child, it bubbles up to the parent, and the listener catches it.
Inside the listener’s callback function, the target property always refers to the original element that triggered the event. You can use this to decide how to respond—for example, using a data attribute (like data-id) to reference a specific object or action.
05. What are closures, and how do they help organize code?
JavaScript functions rely on a lexical environment—meaning a function can access variables defined outside of itself, but variables defined inside the function are only accessible within it.
Take this example: if you call a function outer(), it might log “I love Web Designer!”—but if you try to reference shout (a nested function) or x (a variable inside outer()) outside of outer(), both will be undefined. A closure is the combination of a function and its lexical environment—in this case, the outer() function and the variables it contains.

Closures are invaluable for organizing code because they let you create private variables and functions. This works similarly to object-oriented languages like Python: anything declared inside a closure won’t leak into other parts of your code, so you can build multiple components without worrying about conflicts. The module pattern (a popular JavaScript design pattern) uses closures extensively to structure how modules interact.
06. What does ‘use strict’ mean at the top of a code block?
ES5 introduced an optional mode called strict mode (enabled by adding ‘use strict’ at the top of a file or function). Strict mode fixes quirky behavior from older JavaScript versions by turning common mistakes into errors—preventing unintended bugs.
For example: if you try to access a variable that doesn’t exist (like myVar = 5 without declaring it first), non-strict mode will silently add myVar to the global scope. This can overwrite existing functionality and cause hard-to-debug issues. In strict mode, though, this throws an error immediately—stopping the problem before it spreads.

ES2015 modules use strict mode by default, but for function-based closures, you can add ‘use strict’ to individual functions or entire files.
07. What is “hoisting” in JavaScript?
JavaScript is unique because it doesn’t need to be compiled before being deployed—browsers compile scripts on the fly as they load them. Here’s how it works:
- First pass: The browser scans the script, notes all declared functions and variables, and stores their locations.
- Second pass: The browser executes the code, using the notes from the first pass to “know” where functions and variables live.

During execution, function and variable declarations are “hoisted” (moved) to the top of their containing block—even if you wrote them later in the code. That’s why you can call a function like welcome() before you define it in your script: the browser already hoisted the declaration during the first pass.
08. What’s the difference between an arrow function and a regular function?
ES2015 brought many changes to JavaScript, and arrow functions (() => {}) quickly became popular for their concise syntax. But their biggest difference from regular functions has nothing to do with length—it’s how they handle the this keyword.
Arrow functions don’t create their own this value. Instead, they inherit this from the surrounding code block. For example: if you’re looping through an array with setInterval(), an arrow function would log this.x correctly (e.g., 1, 2, 3…), while a regular function would log NaN (because its this refers to the setInterval context, not your array).

Another key difference: arrow functions have an implicit return. If the function body is a single line (like (a, b) => a + b), it automatically returns the result. Regular functions need an explicit return statement—otherwise, they return undefined.
09. When should I use let and const instead of var?
ES2015 replaced var with let and const as the preferred ways to declare variables—and for good reason: let and const are block-scoped, meaning they only exist within the block (like a for loop or if statement) where they’re defined. This prevents variables from leaking into other parts of your code and causing conflicts.
- Use const for variables that won’t change (e.g., const API_URL = “https://example.com”). If you try to reassign a const variable, JavaScript throws an error. Note: This only applies to the variable itself—you can still modify the contents of a const object or array (e.g., const user = {}; user.name = “Alice” works).
- Use let for variables that will be reassigned (e.g., let count = 0; count += 1).

Unlike var, let and const aren’t hoisted to the top of their block. You can’t reference them before they’re declared—this gap between the start of the block and the variable’s declaration is called the temporal dead zone, a common source of confusion for new developers.
10. What is functional programming, and how is it different from other approaches?
Functional programming is an alternative to traditional object-oriented programming (OOP). Instead of storing state in objects and updating it as the page changes, functional programming passes state exclusively through functions—and avoids side effects (changes to data outside a function’s scope) entirely.
The goal is to create code that’s easy to test and debug. Here’s what makes functional programming unique:

- Pure functions: These functions are unaffected by external data. Given the same inputs, they always return the same output (no surprises!).
- No shared state: Functions never access or modify data outside their own scope. Any state they need is passed as a parameter.
- Immutability: Data isn’t changed after it’s created. Instead of updating an object, you return a new copy of the object with the changes.
ES2015 made functional programming easier in JavaScript with methods like map(), filter(), and reduce(). While languages like F# have used this paradigm for decades, JavaScript’s adoption has made it a go-to for building clean, scalable web apps.
11. How can JavaScript improve my site’s performance?
Most web browsing now happens on phones and tablets—and slow, janky sites drive users away. The good news is JavaScript offers simple ways to boost performance. Here are four key strategies:
Use passive event listeners
Janky scrolling is a red flag for poor performance. Often, this happens because the browser is waiting for an event listener (like wheel or touchmove) to finish before it scrolls—since these events can cancel scrolling by default.

Fix this by adding a passive: true option when you create the listener (e.g., element.addEventListener(‘scroll’, handleScroll, { passive: true })). This tells the browser it can start scrolling immediately, since the listener won’t cancel it.
Note: This replaces the older useCapture boolean. Use feature detection to ensure compatibility, and for intentional scroll locking, use CSS’s touch-action: none (supported in most browsers).
Throttle high-frequency events
Events like scroll, resize, and mousemove fire nonstop to keep up with user actions. If your listener runs resource-heavy code (like updating the DOM) every time, it can grind your page to a halt.

Debouncing (a type of throttling) limits how often the listener runs. For example, you could set it to run only 5 times per second instead of 60. The exact implementation varies by project, but even a small reduction in frequency makes a big difference.
Leverage IntersectionObserver for viewport detection
Many sites use the scroll event to check if an element is in the viewport (e.g., for lazy loading images). But calling getBoundingClientRect() (the old way to do this) forces the browser to recheck the entire page layout every time—slowly.
The IntersectionObserver API fixes this. It runs in the background and notifies you when an element enters or exits the viewport. For infinite-scroll sites, you can use it to recycle old elements or load new content—no layout rechecks needed. It works in all major browsers except Safari (use a fallback for older Safari versions).
Offload heavy tasks to web workers
JavaScript runs on a single thread—so if you’re processing large datasets or editing images, the browser window can freeze until the task finishes.
Web workers solve this by running code on a separate thread. They let the main thread (which handles the UI) stay responsive while the worker handles heavy lifting. Workers can’t access the DOM or some window properties, but you can send data between the worker and main thread using a simple message system.
12. How can I future-proof my JavaScript code?
JavaScript’s core principle is avoiding breaking changes—most code written today will still work in 10 years. But “working” doesn’t mean “maintainable.” Here’s how to ensure your code stays usable (and understandable) down the line:
Ditch spaghetti code
When you start a project, it’s tempting to write all your code in one place. But this couples all your functionality together—if you need to reuse a feature elsewhere, you’ll have to copy or rewrite it. Bugs become a nightmare to fix, since you’ll have to update every copy of the code.
Instead, keep code modular. Use the module pattern or ES2015 modules to split functionality into reusable chunks. Changes to one module will apply everywhere it’s used—no more duplicate work.
Stay framework-agnostic
Frameworks like React and Vue make building components easy, but they often lock you into their specific way of doing things. If you switch frameworks (or even update to a new version), you might have to rewrite entire components to fit the new rules.
Whenever possible, use native JavaScript features. For example, handle data manipulation with plain objects before passing it to a framework component. This minimizes friction when switching tools—and makes your code reusable in Node.js (for server-side work) too.
Keep code clean and self-documenting
Clean code is easy to debug. Use descriptive variable names (e.g., userIndex instead of i in loops) to make your code “self-documenting”—you won’t need comments to explain what’s happening.

Most importantly, be consistent. Pick a style guide (like Airbnb’s JavaScript Style Guide) and use tools like ESLint to enforce it. Uniform code is readable code—even for developers who didn’t write it.
Design for scalability
A single directory works for small projects, but as you add files, it will get chaotic. Create a structure that grows with your project: for example, store all user-related modules in a users/ folder, or split code into models/, views/, and controllers/ for single-page apps. The best structure depends on your project—but plan ahead.
Build testable code
Small, modular components are easy to test. Use tools like Jest or Mocha to write unit tests: define an input, expect a specific output, and let the tool check if your code delivers. Run tests regularly to catch side effects from changes.

For larger projects, add integration tests (to check how modules work together) and functional tests (to simulate user actions). The more you test, the more confident you’ll be deploying updates.
今天用明天的语法写
The best way to future-proof code is to use the latest JavaScript features—even if older browsers don’t support them yet. Babel (a transpiler) converts modern code (like ES2020 or ESNext) into older syntax that works everywhere.

ES2015+ features like arrow functions, promises, and async/await make code cleaner and more readable. Babel lets you use them today, and when browsers catch up, you can drop the transpilation step. It’s a win-win for now and later.
