For most of the problems while debugging, I mostly read my own code and try to make a sense of what I've written instead of blissfully starting to debug (which wouldn't make sense if you had written wrong code logic). The best way to ensure this is TDD. I've only realised this recently and I've started learning how to simultaneously test code before / while I write it.
If everything seems right there, I go for the call stack. But especially for lazy loaded / set values, debugger is the best bet. For example read this conversation and see the mistake I made (also tell me if you understood the last pic a guy posted there, I couldn't figure out if it was a joke, or what the joke was if it was 😅)
Good Error Handling in the code. I recently made a small automatic comment syncer component/app for an interview process. While making it, I learnt what good error handling can do for you.
Following the point, know what kind of errors can appear generally in the type of application you are making. While having my hands on server side programming for the first time in the above mentioned app, I didn't know what http status codes meant what, what kind of errors are there in Javascript, different kind of errors generally. As a person who identifies themselves as beginner / intermediate or between, this can help save time.
For UI bugs, if I know which part of the UI the bug is happening (say it's in hashnode's comment / post markdown editor), I can specifically break on the code that is fiddling (adding a node, changing attributes, subtree modifications) with that part of the UI.
It's very similar concept to what Mutation Observer API enables one to do, but for debugging purposes
Conditional Debugging — for example a use case could be – only break if an xhr request contains a specific string.

PS: This seems like a wonderful resource for error handling in nodejs / javascript. Since I mentioned error handling a lot, it would be worth checking out (I'm yet to)