Written by Ivy Walobwa✏️
As your web app grows in complexity, it becomes essential to master the art of debugging.
Effective JavaScript debugging involves more than just fixing errors. It requires an understanding of how your code works under the hood to ensure your app runs smoothly and delivers the best user experience.
Minified code, which is the version of your code that reaches users in production, is optimized for performance. However, minified code can be a nightmare to debug. When users encounter errors, reproducing and diagnosing issues in minified code is often challenging.
However, with the right tools, JavaScript debugging can become much easier. This article will explore how to leverage source maps to debug minified code and dive into other techniques using Chrome DevTools to efficiently identify and resolve issues in your web app.
Example app
We’ll work on a simple app that increments a count and logs it onto the console. This app demonstrates how minified code can make debugging tricky and how source maps can help simplify the process.
Create the .js files below and add the code snippets as shown:
1. src/counterCache.js
export const countCache = { previousCount: 0, currentCount: 0, totalCount: 0 } export function updateCache(currentCount, previousCount) { countCache.currentCount = currentCount; countCache.previousCount = previousCount; c ountCache.totalCount = countCache.totalCount + countCache.currentCount; }
2.src/counter.js:
import { updateCache } from './counterCache.js'; let count = 0; export function incrementCounter() { count += 1; const previousCount = count; updateCache(count, previousCount); }
3.src/index.js:
import { incrementCounter } from './counter'; import { countCache } from './counterCache'; const button = document.createElement('button'); const previousElement = document.getElementById('previous'); const currentElement = document.getElementById('current'); const totalElement = document.getElementById('total'); button.innerText = 'Click me'; document.body.appendChild(button); button.addEventListener('click', () => { incrementCounter(); previousElement.innerText = countCache.previousCount; currentElement.innerText = countCache.currentCount; totalElement.innerText = countCache.total(); });
In your package.json file, add the webpack packages as shown below then run npm i to install them. We'll use webpack as part of the build process to generate minified code for production:
"devDependencies": { "webpack": "^5.96.1", "webpack-cli": "^5.1.4" }
To enable code minification, add a webpack.config.js file with the following snippet. Setting the mode to production tells webpack to apply optimizations such as modification:
const path = require('path'); module.exports = { mode: 'production', // Enables optimizations like minification and tree-shaking entry: './src/index.js', // Specifies the entry point of your application output: { path: path.resolve(__dirname, 'dist'),// Defines the output directory for bundled files filename: 'bundle.js',// Specifies the name of the bundled file }, };
Now run npx webpack to bundle and minify your code. The dist/bundle.js file is generated with content as shown below. Minification obscures variable and function names, and removes unnecessary characters like whitespace, comments, and unused code, making the output file smaller and faster to load:
(()=>{"use strict";const t={};let e=0;const n=document.createElement("button"),o=document.getElementById("previous"),u=document.getElementById("current"),r=document.getElementById("total");n.innerText="Click me",document.body.appendChild(n),n.addEventListener("click",(()=>{var n,c;e+=1,n=e,c=e,t.currentCount=n,t.previousCount=c,t.totalCount=t.totalCount||0+t.currentCount,o.innerText=t.previousCount,u.innerText=t.currentCount,r.innerText=t.total()}))})();
Next, update the index.html file to reference the bundled output, ensuring your application uses the minified code:
<meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Web Debugging Example</title> <link rel="stylesheet" href="styles.css"> <h1 id="Web-Debug-App">Web Debug App</h1> <p>Check console for bug</p>
Previous count | Current count | Total count |
---|---|---|
Finally, run the app and check the console after clicking the button. To preview the app locally, you can use the Live Server extension in VS Code: The error in the console, t.total is not a function, is difficult to interpret. Clicking on the file in the console does not help pinpoint the issue due to the compact and obfuscated nature of minified code. Identifying the root cause of such an error in a large codebase can be frustrating and time-consuming, as the minified code obscures the original logic and context. 8 JavaScript debugging strategies for web appsLet’s demonstrate eight methods to help make JavaScript debugging a bit easier: 1. Source mapsSource maps are files that map your minified code back to the original source code. They make debugging easier and help investigate issues in production. The file names of source maps end with .map. To generate source maps using webpack, update the webpack.config.js file as follows: The devtool: 'source-map' or devtool: 'eval-source-map' line tells webpack to generate an external .map file which maps the minified code back to your original source code. The source map file URL is also added to the minified code in the bundle.js file. Now run npx webpack. The .map file will generate alongside your minified bundle. Serve the application using a local server, and open it in an Incognito browser window. This prevents browser extensions and cached files from interfering with your debugging process. With source maps generated, the following observations are made:
The exact code and file causing the bug are easy to identify using source maps: With the clear error above, we are able to fix the error and access the correct property on countCache. Our guide on how to use Chrome DevTools should provide a great start. To open the Developer resources tab, click on the More icon, then More tools then Developer resources. This tab allows you to view the source map load status and even load source maps manually: The code snippet below fixes the bug on the console. Update your code, then run npx webpack to compile the changes. Once completed, serve the application and view the updated output in the table: totalElement.innerText = countCache.totalCount; Clicking the button currently updates the previous count, current count, and total on the table. The previous count is supposed to return the previous value of count and the total count is to return the sum of all count values. At the moment, the previous count displays the current count while the total count is stuck at one. In the next section, we’ll explore additional JavaScript debugging techniques, such as using breakpoints and stepping through the code, to identify and fix this issue: 2. BreakpointsBreakpoints allow you to pause the execution of your code at specific lines, helping you inspect variables, evaluate expressions, and understand the code flow. Depending on your goal, there are different breakpoints you can use. For instance:
In our sample application, we’ll apply a breakpoint to the incrementCounter function. On the Sources panel, open the counter.js file and click to the left of line six. This sets a line-of-code breakpoint after the count is increased: We’ll set another breakpoint at line five and edit it. To edit our breakpoint, we’ll right-click on the highlighted section and then click on Edit breakpoint: We’ll set the breakpoint type to Logpoint, then enter the message to be logged to the console: By clicking the button, our application pauses at the line-of-code breakpoint and prints a debug log on the console from the Logpoint set: From the image we can see the following sections:
With this, we can debug our app further. 3. Scope panelThe scope panel can be effective for JavaScript debugging, as it allows you to see variables from the original source: We can see the following scope variables:
From the scope panel and the log point breakpoint, we can see that the current count is one while the count before the increase is zero. We therefore need to store the count before the increment as the previous count. 4. Stepping through code (_s_tep into, step over, step out)Stepping through your code involves navigating through the program in different ways during JavaScript debugging:
You can use the debug controls to step through your code. The Step control enables you to run your code, one line at a time. Clicking on Step will execute line six and move to line seven. Note how the value of previousCount changes in the scope: The Step over control allows you to execute a function without going through it line by line: The Step into control allows you to go into a function. In the function, you can step through the code line by line or Step out of the function as shown below. Stepping out of the function will finish the execution of the remaining lines: To fix our issue, we’ll update the code as shown below. This now displays the previous count on the table correctly: export const countCache = { previousCount: 0, currentCount: 0, totalCount: 0 } export function updateCache(currentCount, previousCount) { countCache.currentCount = currentCount; countCache.previousCount = previousCount; c ountCache.totalCount = countCache.totalCount + countCache.currentCount; } 5. The call stackThe call stack shows the sequence of function calls that led to the current point in the code. Add a new breakpoint in the counterCache.js file as shown, then click the button. Observe the call stack panel: There are three function calls made when the app executes line six of counterCache.js. To observe the flow of any functions in the stack, you can restart their execution using Restart frame, as shown below: 6. Ignoring scriptsWhen debugging, you may wish to ignore certain scripts during your workflow. This helps skip over the complexities of code from libraries or code generators. In our case, we want to ignore the counter.js script while debugging. On the Page tab, right-click on the file to be ignored and add the script to the ignore list: Running the app and pausing on the breakpoint, we can see the incrementCounter function is now ignored on the call stack. You can hide or show the ignored frames: You can group your files in the Pages tab for easier navigation as shown in the image below: 7. Watch expressionsWatch expressions let you track specific variables or expressions as your code executes, helping you monitor changes in real time. You can add expressions like countCache to monitor the value as you step through the code: 8. Debugging code snippetsTo try to fix the bug with the total count, you may run code snippets on the console to understand the logical error. When debugging code that you run repeatedly on the console, you can make use of Snippets. On the Snippets tab, add a sample debug script, save the script then click Enter to run the script: You can observe that the expression with the bug needs to be rearranged to fix the issue: export const countCache = { previousCount: 0, currentCount: 0, totalCount: 0 } export function updateCache(currentCount, previousCount) { countCache.currentCount = currentCount; countCache.previousCount = previousCount; c ountCache.totalCount = countCache.totalCount + countCache.currentCount; } You can explore additional resources on debugging web apps such as this article on debugging React apps with React DevTools, which offers valuable insights into debugging React-based applications. Additionally, this guide on debugging Node.js with Chrome DevTools provides tips for debugging server-side JavaScript using watchers and other advanced DevTools features. These resources can complement the techniques discussed here and broaden your understanding of debugging web apps. ConclusionThis tutorial explored debugging minified code busing source maps and Chrome DevTools. By generating source maps, we mapped minified code back to its original source, making it easier to debug our web app. Chrome DevTools further enhanced the JavaScript debugging process with methods such as breakpoints, stepping through code, watch expressions, and more. With these tools, developers can efficiently debug and optimize their applications, even when dealing with complex, minified codebases. The complete code for this project can be found on GitHub. Get set up with LogRocket's modern error tracking in minutes:
NPM: import { updateCache } from './counterCache.js'; let count = 0; export function incrementCounter() { count += 1; const previousCount = count; updateCache(count, previousCount); } Script Tag: import { incrementCounter } from './counter'; import { countCache } from './counterCache'; const button = document.createElement('button'); const previousElement = document.getElementById('previous'); const currentElement = document.getElementById('current'); const totalElement = document.getElementById('total'); button.innerText = 'Click me'; document.body.appendChild(button); button.addEventListener('click', () => { incrementCounter(); previousElement.innerText = countCache.previousCount; currentElement.innerText = countCache.currentCount; totalElement.innerText = countCache.total(); }); 3.(Optional) Install plugins for deeper integrations with your stack:
Get started now. |
The above is the detailed content of How to master JavaScript debugging for web apps. For more information, please follow other related articles on the PHP Chinese website!

JavaScript is widely used in websites, mobile applications, desktop applications and server-side programming. 1) In website development, JavaScript operates DOM together with HTML and CSS to achieve dynamic effects and supports frameworks such as jQuery and React. 2) Through ReactNative and Ionic, JavaScript is used to develop cross-platform mobile applications. 3) The Electron framework enables JavaScript to build desktop applications. 4) Node.js allows JavaScript to run on the server side and supports high concurrent requests.

Python is more suitable for data science and automation, while JavaScript is more suitable for front-end and full-stack development. 1. Python performs well in data science and machine learning, using libraries such as NumPy and Pandas for data processing and modeling. 2. Python is concise and efficient in automation and scripting. 3. JavaScript is indispensable in front-end development and is used to build dynamic web pages and single-page applications. 4. JavaScript plays a role in back-end development through Node.js and supports full-stack development.

C and C play a vital role in the JavaScript engine, mainly used to implement interpreters and JIT compilers. 1) C is used to parse JavaScript source code and generate an abstract syntax tree. 2) C is responsible for generating and executing bytecode. 3) C implements the JIT compiler, optimizes and compiles hot-spot code at runtime, and significantly improves the execution efficiency of JavaScript.

JavaScript's application in the real world includes front-end and back-end development. 1) Display front-end applications by building a TODO list application, involving DOM operations and event processing. 2) Build RESTfulAPI through Node.js and Express to demonstrate back-end applications.

The main uses of JavaScript in web development include client interaction, form verification and asynchronous communication. 1) Dynamic content update and user interaction through DOM operations; 2) Client verification is carried out before the user submits data to improve the user experience; 3) Refreshless communication with the server is achieved through AJAX technology.

Understanding how JavaScript engine works internally is important to developers because it helps write more efficient code and understand performance bottlenecks and optimization strategies. 1) The engine's workflow includes three stages: parsing, compiling and execution; 2) During the execution process, the engine will perform dynamic optimization, such as inline cache and hidden classes; 3) Best practices include avoiding global variables, optimizing loops, using const and lets, and avoiding excessive use of closures.

Python is more suitable for beginners, with a smooth learning curve and concise syntax; JavaScript is suitable for front-end development, with a steep learning curve and flexible syntax. 1. Python syntax is intuitive and suitable for data science and back-end development. 2. JavaScript is flexible and widely used in front-end and server-side programming.

Python and JavaScript have their own advantages and disadvantages in terms of community, libraries and resources. 1) The Python community is friendly and suitable for beginners, but the front-end development resources are not as rich as JavaScript. 2) Python is powerful in data science and machine learning libraries, while JavaScript is better in front-end development libraries and frameworks. 3) Both have rich learning resources, but Python is suitable for starting with official documents, while JavaScript is better with MDNWebDocs. The choice should be based on project needs and personal interests.


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

MantisBT
Mantis is an easy-to-deploy web-based defect tracking tool designed to aid in product defect tracking. It requires PHP, MySQL and a web server. Check out our demo and hosting services.

PhpStorm Mac version
The latest (2018.2.1) professional PHP integrated development tool

MinGW - Minimalist GNU for Windows
This project is in the process of being migrated to osdn.net/projects/mingw, you can continue to follow us there. MinGW: A native Windows port of the GNU Compiler Collection (GCC), freely distributable import libraries and header files for building native Windows applications; includes extensions to the MSVC runtime to support C99 functionality. All MinGW software can run on 64-bit Windows platforms.

mPDF
mPDF is a PHP library that can generate PDF files from UTF-8 encoded HTML. The original author, Ian Back, wrote mPDF to output PDF files "on the fly" from his website and handle different languages. It is slower than original scripts like HTML2FPDF and produces larger files when using Unicode fonts, but supports CSS styles etc. and has a lot of enhancements. Supports almost all languages, including RTL (Arabic and Hebrew) and CJK (Chinese, Japanese and Korean). Supports nested block-level elements (such as P, DIV),

ZendStudio 13.5.1 Mac
Powerful PHP integrated development environment