1. Scope
Scope determines the accessibility of variables in JavaScript, distinguishing between module scope and sibling function scopes.
Watch section from 0:30 →Engineering video
11:54 · 19 September 2024
Scope determines the accessibility of variables in JavaScript, distinguishing between module scope and sibling function scopes.
Watch section from 0:30 →A function in JavaScript encloses its lexical context, retaining access to variables in its creation scope even when executed elsewhere.
Watch section from 1:17 →The event loop manages the execution of synchronous stack instructions, microtasks, and repainting the browser UI.
Watch section from 2:38 →Promises handle asynchronous operations to avoid callback hell, allowing cleaner chaining with .then, .catch, and async/await syntax sugar.
Watch section from 5:05 →JavaScript module systems evolved from IIFEs and CommonJS to ES6 modules, enabling static analysis, tree shaking, and dead code elimination.
Watch section from 8:00 →Let's talk about the top five most frequent coding concepts for JavaScript interviews. We will go over scope, closures, the event loop, promises, and finally, the module system. I'm Bogdan, there is person right here, together with my brother we started the senior dev in the last four years we've been coaching 289 senior JavaScript developers, help them get to senior level and beyond and get amazing offers even in today's crazy developer market.
Let's get going. Under the number one concept, it's scope, you really want to understand at any point in which scope it's something available. A senior developer immediately by looking at the code knows that this variable right here, it's in what we call the module scope, it's inside and it's available to this module, but it's not available to any other module. And they also know that for example, this inner variable, it is not available in the scope of this other function, because they are in scopes that are siblings.
They're at the same level. This inner variable, it's inside this scope, which makes it not available to this other one. Remember, bad developers care about code, good developers care about data and data in JavaScript lives in scopes. Next concept, it's closures. Whenever we create a function in JavaScript, it will enclose the lexical context, which basically means all these scopes where it was created, that means this create counter function will always have access to the app name variable.
Because app name, it's in the module scope where this function was created. But the same goes for very nested functions, if we look at the increment function, regardless of where you will use this function, you'll always have access to discount variable because count, it's in the local scope where function increment was created. if you use the increment here or you create increment in a totally different file, let's say you import this function, you'll always be able to access this.
All these variables will keep be available in the memory for as long as someone still needs the increment function. The garbage collection algorithm will not free this allocated memory. And by the way, if you are a JavaScript developer interviewing right now, or you want to find your technical gaps in order to get to senior level, take our free 10 minutes technical assessment. It will basically show you what are your gaps and how you can close them to get to senior level or help you prepare for upcoming interviews.
Link it in the comments. Third concept, it's the event loop. And the event loop is the way JavaScript executes code. The easiest way to explain this is by trying to see how this piece of code will be executed. Basically, whenever we have synchronous code like function calls or instructions, they will get added to the stack and so basically, we will add basically the instructions here and then start executing them until we finish.
So, in the case of the console log, the first thing we will log in this case to the console would be this word start. Now, moving on to timer functions, these are a bit more complicated. The reason is because they actually enable us to run things asynchronously. So, even if in this timer function, we say we will execute this in 0 seconds, it will not get pushed to the call stack directly. The callback, it will be pushed to the micro task and it will stay there until we finish.
Everything in the stack and anything that might be in the micro tasks. That, it's what's called an event loop tick. Basically, we execute everything in the stack, we execute everything in the micro tasks. We render the UI, so we repaint the interface and the users see that we're actually responsive. And that's it, and we finish the tick and then we move on to the next micro tasks. things like on click events or on keyboard events, pretty much anything that gets scheduled there.
Moving on, we have another edge case in asynchronous programming with JavaScript, which is a case in which a promise object will resolve. And we have this callback and this other callback to run. Again, those things will not be added directly to the stack, but be pushed to the micro tasks. So, both promise one and two are pushed over here, and they'll stay there until we finish everything in the stack.
So, we still have this other thing, which is console.int, which will now execute. So, basically, we had start executing in the first place. We had end executing in the second place. And now we start executing what's in the macro tasks and micro tasks. So, basically, we will execute promise one, which is the first thing. In our micro tasks, so that will be number three. Then we execute the number four, and now we finish the event loop tick.
We repaint the browser and finally we execute the callback offset timeout that was added to the micro tasks, that will be number five. Next concept, it's promises and we've already seen a bit about promises when we were talking about the call stack. A promise, it's basically an object that will resolve in the future. It will return a value at some point in the future. When we create the promise, it will always be in the status of pending and then it can either be fulfilled or rejected.
The reason why promises were invented was to handle asynchronous programming in JavaScript. Back in the days, before promises, we could only have a synchronous tasks running with callbacks. Basically, if we wanted some code to run after something some has finished, we would pass that code as a function. We would wait and do our asynchronous operation. In this case, it's just a timeout and finally invoke our callback.
The problem with this is that whenever you go to many levels, it can become extremely hard to debug and you can have very hard errors. And it's extremely hard also to do error handling. If we look at a more heavy example, imagine we have a file function that reads a file asynchronously. Then it calls a callback. Which in this case would be another function that validates the data, which can also call a callback.
And then there's a transform data, same procedure and send data. This is kind of a file processor a mock file processor, let's say. Now, to use all this with the callback uh notation, you'll end up having this kind of function call. We will read a file, then pass the next callback, then validate data would have its own callback, and this is what we call callback hell. As you see, if we have any exception around here, it's extremely hard to debug.
This is very nest it, so talking about closures and in closing and scope, it's extremely hard to understand what are the variables here and read this code. This is what they call callback hell. To solve this, we can use promises. And with promise notation, we can rewrite all these functions to return a promise. Promises are smarter objects and we can use them like this. We can actually have one promise that it's from read file, and then on that promise call validate data, which returns another promise, so we can use dot then again.
Then transform data and then again, send data to server and finally complete it. And we can use .catch to handle errors. This is a lot easier to understand and it's a lot easier to read. But guess what, JavaScript also introduced based on promises a sync away, which is just syntax sugar, it's a better way to write synchronous code like this one that looks asynchronous. And if we go back and rewrite this, it will look like this, where file data will do something, then we validate, then we transform, and then we do the result.
This looks like synchronous code one after the other. module that is the event loop and runs asynchronously. What do you want to take out of this? I wouldn't even think about interviewing if I wouldn't understand what callback notation is, what promises are, what callback hell is, and how to use the promise constructor in this case to make a function return a promise. If you go an interview and today's market without knowing those things, you'll be wasting your time. Make sure you understand them, make sure you're able to translate from these both formats, understanding scope, closures, promises, and the event loop, it's not enough. We also need to know very well the module system. Basically, JavaScript used to be a language where we had no modules, we just added scripts to a web page. But sooner developers understood that we do need modules. We do need a way to isolate code and be able to reuse code. And the first way to do that was by using immediately invoked function. Remember our conversation on scope. Basically, inside this function, we have a function scope, every variable inside here it's only available in this scope. And so, a way for us to isolate things is to just create a function that it's invoked immediately, which means everything inside here will not be accessible to the outer world because pretty much no one can actually run that function again. It's basically, it's not even named, it will just run, and we have a way of a kind of private namespace, which is what modules give us, isolation. And we can have another module it's using the exact same variable name, but remember these two scopes are at the same level. They are totally different and that's why this variable has nothing to do with the other one. So, this would work, this used to be the way jQuery was written, however, it was extremely robust and an evolution of this was common JS modules, which I have over here, where we can actually declare variables.
Then we have functions in a function in a file, then we can add them to the module exports. This is what's available from this function and whoever wants to use this can use the require function to get it. The problem here is that it's still far away from a modern module system and there's so many limitations. For example, we can't really do static analysis of our code. Static analysis it's a way for us to somehow before executing this module know certain things about it.
So we can do for example things like removing that code, aka tree shaking. You cannot do that with require because this code, this require it's a function and it'll only execute when you run the code. And this is one of the problems with the old common JS modules. Node JS it's still running mostly on common JS. Now, the latest evolution of this was the ES6 modules. Which is what you probably been using if you write code today where we annotate, we declare what a module exports and then we can use the import keyword to import different things.
The cool thing about these ones is that module bundlers like Webpack, Vite can actually look at these imports and exports and see if there's anything in our code we're not actually using. What we call dead code, and they can actually drop that, they can remove that at build time and have a much smaller bundler and a much more performant application. There are many benefits of static analysis and import and export allows us to have this modern module system with static analysis where we can actually understand, you know, how our modules are being structured.
And on the structure, which is what we call a dependency graph to certain transformations. Knowing how the module system works will solidify your knowledge, not only of writing JavaScript code, but how you can actually package and ship that to production.