Debugging code is one of the most important skills you can learn since it will make you a faster and more competent developer. In this video I go through the exact process of how to debug common errors, various console logging methods, how to use a debugger, and multiple tips and tricks that make debugging much easier.
WebDevSimplified’s courses on Frontend Masters: https://frontendmasters.com/teachers/webdevsimplified/
Find Frontend Masters Online:
Twitter: https://twitter.com/FrontendMasters
LinkedIn: https://www.linkedin.com/company/frontend-masters/
Facebook: https://www.facebook.com/FrontendMasters
Instagram: https://instagram.com/FrontendMasters
TikTok: https://tiktok.com/@frontendmasters
About Us:
Advance your skills with in-depth, modern front-end engineering courses — our 250+ high-quality courses and 24 curated learning paths will guide you from mid-level to senior developer! Start your path to mastery today: https://www.frontendmasters.com/?utm_source=youtube&utm_medium=home_link&utm_campaign=exlusive
Оглавление (7 сегментов)
Segment 1 (00:00 - 05:00)
There are millions of tutorials out there teaching you how to build amazing big projects, but almost no videos touch on how do you actually debug your code. In this video, I'm going to talk about how you can debug your code. From basics of understanding how to fix syntax errors, all the way to understanding how these different debug messages work, what is a stack trace, how can you use the debugger built into your browser with all these really amazing features as well as various other things. I have a whole list of different stuff that I want to cover and this is essentially a crash course on debugging. Welcome back to WebDev Simplified. My name is Kyle and my job is to simplify the web for you so you can start building your dream project sooner. The very first thing I want to dive into is how you actually understand the different error messages that you're given because oftent times they're quite confusing and hard to work with. For example, we're getting an error message here that says uncaught syntax error unexpected identifier price and it tells me that is at script. js55. And really all this is trying to tell me is what the error is as well as where in my code that error is located. Often times when you see an uncut syntax error, this essentially means that you have a problem in how you actually wrote your code. And oftent times in your editor, as you can see here, it'll show up with a little red squiggly line underneath of it telling you that there's an error in this particular region. Now, in our case, it says that the error is on line five. And it specifically says it's in column five of our code, and that's what that second colon mean. So, script. js is the file. The first colon tells us the line number, which is five. And the second colon tells us the actual place in that line where it occurs. So in our case, this is line 1 2 3 4 5 and we want to go to column 5. So we have 1 2 3 4 5. It's telling us the error occurs right here essentially at the start of price. And it's saying that this price keyword, it doesn't know what this means. Often times the syntax error you get inside of here won't actually tell you what you need to solve. It'll tell you generally where the problem is in relation to your code, but it doesn't tell you what the actual problem is. Usually if you hover over the error inside your code editor, though, it'll give you a much better error. In our case, it gives us an error that says comma expected. And that's because we forgot to put a comma on the previous line. So even though it said our error was on line five, really the problem was on line four inside of our code. Now, if we save, that's going to solve that particular problem inside of our code. And you'll notice now I'm actually getting a new error. It says uncut syntax error. Again, it's saying unexpected token and this nice curly bracket and that is on line 12. So if we go to line 12, you can see I get the error right here. And again, if I hover over this, I forgot to put a comma and the error was actually on the line before where it said that the error was. So very often when you're running into these different types of errors, it's because the error is either before or after where it actually says it is. Very rarely is the error on the exact line it says it is, but it's generally related to the code in that particular instance. And often times you can get some help by hovering over the error inside of VS Code. Now, another quick example of an error I want to go over before we cover this area. I'll just comment this out. And you'll see if I come down here and just remove this bracket and I click save, I'm going to get another error here. This time it's saying that my error is on line 34. I can actually click on this and it'll bring me to the exact line. You can see my line is right here. product. for is where the particular error is and it's saying again it's unexpected identifier. If I hover over here, it's giving me a bunch of different warnings for different errors, but you can see nothing super concrete. If I hover down here, you can see I'm getting an error, but again, it's not very concrete. Often times when you're having trouble figuring out what the error is, that means you most likely are forgetting either a parenthesis or a curly bracket of some form or even a square bracket. You just have some type of formatting mistake when you don't really get a clear indicator of what the error is. So, wherever you see that, usually you're going to have a bracket that is highlighted in red if you're missing another bracket. So, in our case, this one's highlighted in red, which means I'm missing a matching bracket. So, I can come up here and I can add that bracket back and that'll actually solve that particular error. So, when you're trying to debug syntax errors, it's really helpful to look at where the errors are inside of your editor as well as what the error messages are inside your editor as well as in the console dev tools to really figure out what's going on. And if you ever want to open up these console dev tools, all you need to do is inside of whatever page you're on, this is our page right here, you can just right click on it and go down to where it says inspect, and that's going to open up these dev tools. And you just go to the console section to view all your different errors inside of your console. Now, that pretty much summarizes how syntax errors are handled. Now, I want to handle runtime errors because, as you can see in our code, we have no red squiggly lines anywhere, which means there's no errors in the code that we wrote syntax- wise, but we still have errors in how our code is actually executing. And the two most common errors that you're pretty much going to run into is uncaught type error cannot read properties of null or it'll say undefined. All that means is that you're trying to do something like call a function or do some type of operation on an object that is either null or undefined. This happens all the time when you have an object you expect to have a value but it actually has a value of null. In our case here, it says that we cannot read the properties null reading add eventlister. That means we're trying to call the function addeventlister on an object that doesn't exist and it's on line 29. And if I go here, you can see right here is where our code is. Button. addeventlister. So if we go into our code, you can see button. ventlister. I'm trying to query selector the element with the class. button. And if I look at my HTML, I actually called this class btn instead of btn. So I just need to come in here and change this to btn. And when I say that, you can see that fixes that error. And I move on to another error. And this is another error that you're going to see really often is uncaught reference error. Product is not defined. This means you're trying to reference a variable that's not been defined yet. And this is very common, especially if you're working in
Segment 2 (05:00 - 10:00)
JavaScript instead of TypeScript. You'll run into these errors all the time. And in our code right here, it says it's on line 34, which is right here. And it says product. This should technically say products because as you see up here, we called this variable products. So I just made a typo in my code. So this is telling me, hey, I'm trying to access a variable that doesn't exist. This product variable does not exist yet. So when I try to access it, it's throwing an error for me. And if again I go to that error, you can see it's right here on that line where that product code is. So all I need to do is fix my typo here to be products instead. And now when I save, it actually prints out all the information for my different product related stuff. Now I'll just expand this a little bit so we can fit every single thing on one line. And now I want to talk about a different type of bug. That's one that's really hard to find because it doesn't actually throw any type of error. You'll notice inside of our code, everything is running fine. It's printing everything to our console, but we actually have an error in our code. Essentially, what I'm trying to do here is I'm logging out the product ID, the product name, and the price, but I'm subtracting $10 from the price. So, essentially, it's a $10 discount code that's being applied, and I want to print out what that new price is going to be. And all these are looking fine, you know, laptop, smartphone, tablet. But as soon as I get to the smartwatch, you can see the price prints out n instead of whatever the price should be. And this is sometimes a difficult problem to debug. So, usually what you want to do is you want to work backwards through your code. So, when I run into this particular problem, I go, okay, you know what? My price here should have a value, but it doesn't. So the very first place that I like to look is I go into my code and I say okay where am I logging out that price or using that price. In my case I'm logging it out right here. So I know okay my product price here is for some reason not available. So now I say okay let's go back. It's from this products variable is where my product prices are coming from. So let's go ahead and look at that variable. You can hold down control and click on a value and it'll bring you to that variable definition. And if I look in here my laptop's fine, my smartphone's fine. Tablet's fine. Oh, in my smartwatch I forgot to actually add a price into here. That's why I'm getting that error. So, I can add whatever price I want into here. For example, like $400. And now, if I give that a save and I expand this, you can see my smartwatch has been discounted at $390. These are really difficult problems to actually end up solving. And the way that I generally do it is just by working backwards through my code. Wherever my error is happening or bug in my code, I start there and I work backwards slowly through my code trying to figure out where that error could have come from. And often times when you're doing this, one of the best things you can do is just throw console logs in your code for really easy rough debugging purposes. So, I could go in here and say, you know what? Okay, inside of here, for some reason, something's wrong with my product. So, I'm just going to do a quick console. log and log out my product variable to see what's going on there. If that doesn't help me, I can move it a step up and say, you know what, my products variable. Let's see what that looks. I want to log that out. If that doesn't work, you can move up further and continue to move up the chain, logging different things out until you find, oh, you know what, this variable is a different value than I expect it to be. And that's where you can actually debug your code and fix the particular bugs. Now, kind of the last thing that can really help you with narrowing down a bug, let's come in here. We're just going to change this to product again. We're going to give that a quick save. And you'll notice I get something kind of interesting being printed out on my screen. I get that same error, but you'll notice down below here, I actually get two different sections for where that error is occurring. And this is considered your stack trace. And normally when you have a stack trace, if you click this little arrow over here, it'll expand and show you the full stack trace in an easier to look at way. And all a stack trace is a list of all the different functions that occurred up until the point that you're currently at. So in our case, if we look at our code real quick before we dive back into the error, you can see that we're calling print products and then inside print products is where we're calling our product. Which is where the error in our code is occurring because I spelled product wrong. It should say products instead of product. So if I look over here, this stack trace essentially tells me what are the different steps that occurred to get to this point. And it's in reverse order. So the very first element inside your list is the most recent thing to happen. So that happened is print products was called and an error happened on line 35 in our code. And if I click on that, we can expand this a little bit. You can see right here, line 35 is where that error is occurring. But it also gives me the thing that called that function. In our case, it's listed as anonymous. And that's because there's no other function. This is just inside of a file. So normally, if you have either a function with no name or you just call a function inside of a file with nothing around it, it'll be labeled as anonymous. And when we click on that, that tells us where did we call the code to get to this point. So this gives me a really nice step-by-step function for me to look at how I got to this exact point in my code. I know by looking at this that I called something on line 44 in my anonymous script which I know was print products and then I know inside of print products on line 35 I had this uncaught reference error and this allows me to actually step back through my code because there may be 10 different places where I call print products but only one of them actually causes a bug and when I get that bug I can know which of those 10 is the one that's causing the problem by looking through this stack trace. This is one of the most powerful but underused features inside of debugging. Now, the next thing I want to talk about when it comes to debugging is various different tools that we can use. We've already looked a lot about the dev tools that we have and some things that you can do inside of your code editor. But there's actually some additional features we can use as well. So, first of all, let me fix this error. And I'm just going to add a new error in my code by removing this comma. So, I get an error right here. Comma is expected. And the nice thing is inside of VS Code, we can actually see those errors directly. So, if I click control and the till day key, that opens up the terminal. But in our case, what we really care about is
Segment 3 (10:00 - 15:00)
this problems tab. And if I open up this problems tab, it's probably quite small. So, I will just zoom in real quick so we can really easily see that. You can see it tells me inside of script. js, I have this error right here. It tells me the line, the column number, what the error is. So, it just gives me an overview of all the different errors across all my different files. The only thing that's important to understand about this is if you close a file, it only shows the files you currently have open. So, it'll only show you errors in the files you have open. But, it's kind of a one-stop shop where you can look at all the errors inside your codebase. And this will work for various different things. If you're using a llinter like ESLint, those will show up here. If you're using something like prettier, these will show up here. Essentially, any extension or error inside your code that causes squiggles to show up underneath your text, whether it's a warning or an error, those will all show up inside this problems tab, which is really useful to work with. Now, let's get our zoom level back so we can just be a little bit more zoomed out. There we go. Close out of that. And we'll reopen that tab that we just closed. I actually want to talk about the second thing that we can do to actually fix our code, and that is using TypeScript. Typescript allows us to see a lot of different errors immediately without having to wait for them to actually show up inside of our console dev tools. So it can help us with syntax errors as well as runtime errors as well. So what I'm going to do is I'm going to open up this file called script ts. This is going to immediately show me a bunch of errors because it's essentially conflicting with this file. So let's comment out this entire file and we can come over to here and you'll notice immediately I'm getting different errors is showing up inside my code. I removed that price from smartwatch here. And immediately when I come down here, I'm getting an error because price is either a number or undefined. So, TypeScript is smart enough to know, hey, you forgot to put a price on this. I'm going to throw you an error saying that you need to make sure you handle this because it could be null. And if I know that I always want a type to be on this for a number, I can actually manually type this. It's not super important what this syntax looks like, but I can, for example, say that I always want to have a price that is a number on this object. And now I'm going to get an error down here because I don't have a price. And as soon as I add a price, whatever number it is, now that error goes away. So, TypeScript just adds a little bit of extra safety into my code for figuring out what my different types for things are going to be. Same thing down here. This button could be an element or null because, for example, I could type in a bad selector here that selects nothing. So, it's again telling me, hey, make sure you handle this so you don't end up with a runtime error inside your code. This is why I generally write almost all my code inside of TypeScript. I really prefer TypeScript over JavaScript, but it's up to you whichever one you want to use. I just want you to know that Typescript can help you with solving some of these bugs, especially these weird to find runtime bugs that happen from like typos and things like that. Now, the next two debugging tools I want to talk about are going to be much larger. The first one is going to be the dev tools that we've already kind of looked at. We've already looked at the console a little bit, but also there's this sources tab, which allows you to really dive into a lot of really interesting stuff with all these break points and everything like that. That's going to be a large section of the video. But the next thing I want to talk about is specifically related to the console because there's a lot more console methods than just console. log. If I come in here, you can see there are a ton of different methods, all for the console. And I kind of want to go over the most important ones that'll make your debugging experience that much better. Now, I'm going to simplify our code quite drastically by removing almost everything except for this button section. We're just going to have a single selector for our button. And that's because I want to talk about how you can actually view the contents of a element inside of your HTML. And we're on the TypeScript file here. So, let me make sure I go back to the JavaScript file. There we go. So, we just have that single button. And what I can do is I can console. log that button. And you'll notice if I look over on the right side of my screen where I have that button being logged, it just shows me the button element itself, but I can't see any of the properties on this, like what is the inner text, what is all these other properties for the different attributes and so on. If you want to see that additional information, you can use the console. dur or diir function instead. And this will actually log out anything as an actual object instead of as like an HTML element. So anytime you're logging something and it's not showing you exactly what you want, you can instead use console. dur dur to get the object version of that instead. Now I can open this up and I can see every single property on my object and look through exactly what those different values are going to be which makes it easier for me to debug different things inside my element because when I just see it looking like this I can't do any debugging other than this is what my element looks like. Now before I start diving into the individual nuances of the different console methods that we have available, I do want to talk about the most important feature of console log that confuses everyone including myself time to time and that is this whole idea of like live view. So let me go ahead and show you what I'm talking about. We're going to create an object called person and we're just going to set the name to Kyle. Really straightforward stuff. I'm going to console. log that person. So if we look over on the right hand side, you can see my name is Kyle. And when I expand this, you can see it says name of Kyle. Super straightforward. That's exactly what I expect. And if I add additional properties, such as we'll put an age property in here, you can see that's showing up as well. So everything is working like we expect it to. But what happens, let me just close that out. If I come in here and I instead say person. name, and I set that to Sally. Now when I give it a save, and I actually want to do this after my console log, not before. So when I save, you can see the name here is Kyle. The age is 30, just like I expect. But when I expand this, you'll notice the name is actually Sally, and the age is 30. This is one of the most confusing features inside of the logging tools that are built into the browser is that when something is unexpanded, so the very first time you log something, it gives you whatever the value is at that time.
Segment 4 (15:00 - 20:00)
But when you expand something, whether it's an object or an array or whatever else, as soon as you expand something, it's going to give you the most up-to-date value for that. So since we updated the name of person to be Sally, it's giving me the most up-to-date version of what that person is. This is sometimes what you want, but often times when you're trying to debug something, you want to get what the value was at the exact time you put your console log because you're trying to figure out why is this value null or not what I expect. So you want that exact value at that exact time. The best way for you to do this is going to be coming in here and just creating a brand new object. So we can use this thing called the spread operator, which is three dots before our object and then wrap it in new curly braces. And this creates a brand new object that's entirely decoupled from the other object. So now if I just close out of this and refresh, you can see the name is Kyle, age is 30, and when I expand, it still stays those old values cuz I created essentially a brand new version of my object. Now this works great, but it doesn't work for nesting. So if I had another object inside of here, for example, an address that had a street, and we'll just say old, and then I come into here and say person. add address. reet is equal to new. Now, when I save and I expand this, you can see that it's giving me that new value for the address cuz this spread operator only works one level. it doesn't deeply nest itself through. So instead, if you're running into this problem, the best option for you to do is just to do a JSON stringify of whatever your object is. And then you want to parse that. So we can say JSON. parse. This is the quickest and easiest and dirtiest way for you to essentially create a brand new object that's entirely decoupled and it's deeply nested. So now when I give that a save and I expand, you can see my address is set to that old property. No matter what, whenever I expand or unexpand, it's always going to be that old property for me. And this works with objects, arrays, or anything else that expands, like a date, for example, all those different things that expand down. You're going to want to do this trick if you want to make sure you're always getting the old values and not the most up-to-date version of those values. This is something that is really confusing. I've run into this problem a lot until I finally realized what was going on behind the scenes. So, now let's go ahead and rapid fire through some of the really cool console methods available. One of the console methods that I really enjoy is console. t. It essentially allows you to take an object or anything that's tablebased and render that out inside of a table. So, we can come in here with our person and we can just close out of that. And now you can see that I get this person that has a name of Kyle, an age of 30, and then this address that has this street of old. It doesn't work super well with like nested objects like this. But if it's like one level deep, you can see that it kind of gives me the object printed out like this. But the real benefit of where this table works is when you have an array of data. So, let's come in here and I'm going to create an array of people. We'll just call this persons. And then I'll just create a brand new person after here that's going to have a name of Sally. And we'll just throw a random age on here. And now when I give that a save and I make sure I call this persons, you can see that now I'm getting the first element has that name of Kyle and the age of 30. And then here we have the name of Sally and the age of 34. So this works incredibly well when you have array based data that you want to essentially print out and easier to read tablebased format. cuz if I just do a normal console log, you can see it kind of is a little bit more difficult to read what's going on, especially when you have larger, more complex objects. While this table is a really nicely formatted way of looking at it. Now, speaking of formatting, we can actually use grouping inside of our editor as well. You'll notice how we can do this expansion and unexpion of things inside of our editor. We can actually create our own groups when we log things out. And that's by using these group methods. So, we have group and group end are the main two that we want to look at. So, group, you can pass it in a name if you want. It's optional. We'll just say that this is going to be group name, just like that. And then we can console log inside of here. Just normal stuff. We'll say inside group. We'll put another console log that's going to be outside group. And then we'll paste down an inside group too. Just like that. And then whenever you want to end whatever your group is, you can just say group end. And you don't need to pass anything to group end at all. It'll just end whatever the most recent group is. So now when I give that a save, you'll notice I'm getting my outside group printed. And then whatever the name of my grouped item is, group name, followed by all the things that are inside of it. And I can expand and collapse that as I want. And I can even come in here and I can put something outside of here. So we'll call this outside group two. And you can see I have that grouping. Now the group collapsed function just allows you to make the group by default be collapsed. So you can see it starts out collapsed. While if I do the normal group function, it starts out as expanded. So this group function is really useful when you want to essentially log out a bunch of information, but you don't want to clutter up your logs with that information. That's the perfect case for a group collapsed. Give it a save and now you don't have a bunch of stuff in your logs, but if you want to see that additional information, you can expand and see all that extra information. Now, when it comes to formatting your different log message to make them either stand out more or stand out less, the best option for that is being able to change what your logging level is. By default, whenever you do a console. log, that's kind of like your default normal level of information that's being passed along, but we can actually pass along various different levels of information. If we come over into our dev tools, there's a section called default levels. If you're more zoomed out than me, it might show up on the left side of your screen somewhere. But if you just open up this default levels, you can see we have four different levels we can log. We have errors, warnings, info, and verbose. And by default, we log at this info level. So info, if I remove that, you can see removes all the different
Segment 5 (20:00 - 25:00)
logs that we have cuz by default, everything logs to this info level with console. log. But we also have this warnings, the errors, as well as this verbose level where we can do different levels of logging that we want to. So let me just come in here and show you what those different errors look like or those different values. So we have console. log, that's going to be info by default. We also have console. info, which allows you to log to info as well. So we'll just call this one log. this one info. We can come down another level here. We have our console. debug information. This is going to go into this verbose category. So, we'll kind of put this up here so we can put them in order of most important. We have console. warn. This is going to go into that warning category. This one we called debug. And then finally, we have console. And that's going to go into that error category. So, let me just close out of that. Throw in the error. If we give that a save, you can see I have one hidden message and that's because verbose by default is hidden. I'll add that in there so we can see everything. Now you can see I have my debug, my login info which both go on the same level and then we have my warning message and my error message. And the nice thing about warnings and errors is they always come with a stack trace. So you can see I have my stack trace for exactly where those different logs are at because usually if you have a warning or an error, that's because there's a problem inside your code. So you want to know, hey, where did that happen and what is my stack trace associated with that? Debug messages are really great when you want to add a bunch of different debug information essentially, but you don't want it to clog up the rest of your consoles cuz by default you're in the default mode which only does info, warnings, and errors. So if you want to put a bunch of debug logs all throughout your console for various different things for like debugging purposes, you can do that. And then when you get to the point where you're like, hey, I need to look at my debug information, you can just come in here, toggle that on, and now you can see all your debug information on top of your warnings, errors, and so on. So, it's a great way to put like low-level logs that you don't want to see all the time, but may need to look at for specific use cases. Now, one of the most common use cases for the error is when you want to error out if something happens. So, for example, I can say like if some value is true, then I want to do my console error. So, if this value is true, I want to throw my error. And if it's false, I don't want to throw my error at all. This is a really great way for me to be like, hey, sometimes I want to show the error, sometimes I don't want to. So, you're going to see this code all the time, which is why we can actually use the console. assert assert method which essentially does the exact same thing. You can pass it a value of true or false and then you can pass it along all the different stuff you want to log and if it's a value of true it's not going to render anything because it says hey this thing asserted to be true which means it's exactly like you expected it to be so I'm not going to put a log at all but if it's false then I'm going to throw that particular error inside the log. This is really useful for debugging purposes. Let's say somewhere along the lines I have a person. We're gonna get our person of zero and we'll just put it in a variable called person. And somewhere along the lines, my person. name is null for some reason. I don't know why it's null, but it shows up as null for some reason. So what I can do is I can say person. name equals I'm sorry, not equal to null just like this. So now essentially if my person name is not equal to null, that means that this doesn't log anything. But if it is equal to null, it will log something. So in our case, our name is not null, so it doesn't log anything. But if I were to set my name to null, then you can see that it actually logs that error. So what I can do with this console assert is every single place I use my person, so like let's say I use it down here and down here, I know it's null somewhere. I just don't know where. I can throw this log like 5, six, seven different places in my code. And then you'll notice as soon as I actually run my code, one of them or multiple of them will show up as an error. And now I can say, okay, I know that right here my person was null and down null. So it allows me to narrow down exactly where in my code that person name was null. And you can do this with any check that you want. This is just a really great way to narrow down your log. So you're not having a bunch of if checks inside your code. It's just inside your console log. And then as soon as you're done with your console log, you just delete it and everything associated with it is completely removed. Now these last few logs are a little bit more niche, but still somewhat important. We have console. trace. Console. trace is really simple. All it does is log a stack trace for you. So if I was inside like a bunch of functions, I would have a full stack trace for exactly what my stack trace looks like. useful if you just want to see, hey, how did I get to this point in the code? Console. trace is going to be there for you. The next one that I want to talk about is going to be console. count. And all this does is count how many times that you call console. count. So, if I call this function four times, you can see default 1 2 3 and four. And I can even give this a name. So, we'll give this one a name of name and name. And now you can see my name counter has been counted twice while my default as well. So, it just allows me to figure out how many times I've run certain pieces of code, which can be useful. And there's al also the console. count reset which allows you to reset account give it a name and it resets that count as well. So if I were to come down here with another name count you can see it goes one two I then reset it and then it counts back at one again. Lastly we're going to have console time and console. time you can give it a label if you want name like that allows you to actually figure out how long something takes to run. So we can put whatever code we want inside of here and then we can put a console. time end and give it that same exact name. And now it's going to track how long it takes for this particular piece of code to run. So we can just come in here. We'll just do a simple for loop. Let I equal zero. I is less than a large
Segment 6 (25:00 - 30:00)
number here. And we'll just add one. And we'll just do nothing at all inside that loop. So it's literally just an infinite loop that doesn't do anything or not infinite. It runs 10,000 times. And now if I save, you can see it took 0078 milliseconds to run that. And if I throw another loop inside of here, you can now see it's taking slightly longer cuz I have multiple loops inside of here. So, this allows me to figure out how long something takes, which is great for benchmarking different things and figuring out where your code is slow versus fast. Also, you can use the console time log. And what this does is it'll just log out what the time is at this particular location. So, my timer started here and my timer ends here. And I want to figure out in the middle how long has it been. So, now you can see I get two logs. The first one is what is the time at this point? And the second point. Now, for my next step of debugging, we're going to move into the debugger tools built into the browser cuz console is really great for debugging rather simple things. But as soon as your code gets more complex, the best step is to use an actual debugger to debug your code. So, I'm going to change our code back to what we had at the start. We have our products. These are all correct. There's no errors. We can see the price is correct here. And then I have my print function for printing out all my different products and then logging out that different information for my product. And when I save, it's printing all my information correctly. That part doesn't really matter. The important part is we can go over to this sources tab. And this allows us to really dive into exactly what's going on inside of our code. So what you can do on the lefth hand side here is you can click on whatever file you want to debug. In our case, we're going to debug our script. js file. And now we inside here can put a breakpoint anywhere we want in our code. I'm going to put a breakpoint right here. Essentially, you just click on the line number and that's going to add a breakpoint. You can see right here is where all of my different break points are. And I also have different configurations I can do. For example, I can pause on uncaugh exceptions or uncaught exceptions. Generally, I like to pause on uncaugh exceptions. So if I have an error inside of my code that is uncaught, so for example, I don't have like a try catch wrapped around it or a promise wrapped around it, like if I have a syntax error or something, it'll actually pause my code execution exactly where that error occurs and allow me to see what's going on inside my code. So let's actually remove this breakpoint we added. I'm just going to disable it by unchecking it. I'm going to add an error into our code somewhere. So I'm just going to change this to product instead of products. Now when I save, you can see immediately my code gets paused. You can see here it says pause on exception reference error product is not defined. That's that log that normally shows up inside your console, but now it's actually pausing us exactly where that error occurred inside of our code. And the really nice thing is we have access to all the variables at this point in our code. So, you know what? I can see what my products variable is and it gives me all the information inside of here. There's not really much that I can look at, but I can see everything that's inside of here. If I had variables defined inside of here, I could look at all these different variables and see exactly what's going on. And inside my console, I have access to all the local variables. So, let me just come in here and I'm actually going to create a variable so we can see this in action. I'm just going to say const a is equal to 1. Super straightforward. Not even using that variable. And then I'm going to go back into my source and make sure that I click this play button right here. This allows it to finish the execution. We'll get everything finished out. And then I'm going to restart my application. So now I have access to all those different variables. Like I mentioned, you can actually see them on the right hand side of my screen. I'm just going to move out of the way. You can see a is equal to one. Inside of here I have those local variables, script variables, and global variables. All the stuff that you would want to see inside of here. But now if I go into my console, I can log out a and you can see it gives me the value of one because whenever you stop your code execution using a breakpoint like we've done here, it actually stops your code right there and it says, "Okay, I'm stopping my code right here and I have access to every single thing that my program has access to at this time. So I have access to this a variable even though it's inside this function. " So, especially when you're first getting started with coding, I often like to enable this little check right here to pause on uncut exceptions because it allows me to immediately see where this error is and hopefully be able to understand why that error is happening. Now, in our case, let's just fix this bug by changing this to products. And if we give our page a refresh, you can see everything's working. But I want to stop on my custom breakpoint right here where it says const a equals 1. So, if I actually refresh my page, you can see now it's paused me on a breakpoint and it just stopped me at this exact location inside my code. Again, I have access to this scope section down here that has all the different information for all the different values inside my code, which is really useful. I can go to the console and do whatever I want. But the real power of this particular section comes in right here with these various different buttons right here. So the very first one I have is just the resume button. And essentially what this does is it plays through my program until it reaches the next break point that it hits. In our case, we have no other break point. So when I click this, my program just finishes completely. The other ones are a little bit more interesting. I have the step over, the step into, and the step out of functions that I can deal with. And then finally, there's just like step, which is just a normal step. But for the most part, you're going to be using one of these three. Now, step over when you call this, if you're about to go inside of a function, it's going to step over that function entirely. So, let's say right here, we're on our products. And if I didn't care about what happens inside this function at all, I could just use step over. And you can see it steps over this entire function and moves me to the next line of my code. So, it pretends that none of the code inside of here exists. It runs all that code and then just stops me on the very next line of my code. That may not be what you want, though. So, let's refresh here and start at the stop again. You can see now I can use step into. And what step into does is it jumps into every function that I would normally call. So, when I click on this, it jumps into that function onto
Segment 7 (30:00 - 32:00)
the very next line and allows me to loop through everything inside that function. And then again, it loops back through to the very next iteration of that function. And since we have like five different products, we're going to loop through this products thing five different times. You can see every time I click this, we're looping through it five total times. And then once we're done with all five of those, we end up outside that function and then it'll run through the rest of our code. Now, in our particular case, let's say that we do that exact same thing. We step into this products function and instead of having like five products, we actually have a thousand products. Well, clicking this button a thousand times doesn't really make sense. That's where the step out of function works. This essentially takes whatever function we're currently in, removes us from it, runs all the code, and then stops us on the very next line. So if I click on this, you can see it immediately runs all the code inside this function and moves us to the very next line of our code after that function finishes executing. So step into, we can get into that function, look at what we want, and as soon as we're done, we click step out of to get out of that function. Another really nice thing that you can look into is that we have this call stack section right here. So we can see the call stack for exactly every single thing that's happening inside of our code. So as we step through our code, we'll notice that this call stack will actually change. You can see it immediately removed that print function because now I'm no longer in that print products function. So, it allows us to really fine-tune exactly where our call stack is, looking at all the different things that are occurring in our code. This is one of the best ways to really debug complicated problems. Also, something that's really important is this watch section right here. So, this watch section allows you to watch for any variable value and see exactly what it is. So, you can add a watch expression and type anything you want. In our case, we're going to watch for the product variable just like this. And you can see right now there's nothing defined as product because if you look at where our code is stopped, it stopped right here, not inside of here, which is where product is actually defined. So, what we can do instead, we'll just let this finish, refresh our page. I'm going to step into that products loop. So, we're going to come in here, step into that, and now you can see my product down here is defined as an object and has all my different values for my product object itself. And as I step through to the next product, you can see the values inside of here update. This is a great way to watch specific variables. And you don't just have to watch a single variable. I could, for example, say I want to get the product. name. And now this section right here is always going to give me whatever the product name is. And if I just move out of the way, you can see smartphone is what's being listed. So this is a really nice feature to be able to debug through your code, see exactly what all the variables are, play around with the different variables, go into the console to say, you know what, what does my product look like? Here's all my information for my product. This debugger is like the most advanced way of doing your debugging where if you have a difficult problem that console logs not solving for you, debugger is the next place to go to be able to say, okay, you know what? I'm going to step through my code line by line to see exactly what's happening at every single place inside my code. And that will usually let you be able to solve any of the complex bugs that you run into.