JavaScript 101: A Comprehensive Summary of Core Concepts

JavaScript was created in 1995 by Brendan Eich at Netscape. At the time, the web browser was cutting-edge technology that connected everybody on the planet via the World Wide Web. At the time websites were completely static with pure HTML.
It was designed as an easy-to-use high-level language to help developers make websites interactive. Today it's arguably the most popular language in the world and its standard implementation is called ECMAScript, which is the default language in all web browsers. It's the only code that natively runs in a browser besides web assembly.
However, the browser is not the onlyruntime, you can also run JavaScript code on a server thanks to tools like Node.js, Deno, and Bun. As the name implies, JavaScript is a scripting language. That means you can execute code on the fly, by opening up the console in your browser Dev tools, to run some code that changes the appearance of a website at any time.
JavaScript is interpreted line by line as opposed to other languages like C which are compiled ahead of time. However, interpreted is not the most accurate term to use here. Under the hood of the browser, there's an engine called V8. It makes JavaScript run extremely fast by taking your code and converting it to machine code with a process called Just in Time compilation.
Let's jump into some code, to use JavaScript on a web page you'll first need an HTML document, inside of which we'll have a script tag. You can write code inside the tag directly or reference an external file with the source attribute. Now to say Hello World, use console log which is Javascript's built-in tool for printing to the standard output. Now open the HTML file in a browser and see the value printed out in the dev tools.
There are several different ways to Define variables. The most common is LET. Start by giving it a name which will normally be in CamelCase, then assign a value to it. Javascript is a dynamically typed language which means no data type annotations are necessary. In this case, assign a number which is one of the seven primitive data types built into the language. However, we don't need to assign the variable a value immediately because it can be reassigned later.
Without an assignment, it automatically uses the Primitive value of undefined as its default value. However, we can explicitly represent an empty value using Null, and later on we could reassign that same variable to a string. It's an entirely different data type but that's perfectly okay.
Any value that's not a primitive will inherit from the object class but more on that later. Let’s talk about this semicolon. Technically, semicolons are optional because if you leave them out, the JavaScript parser will add them automatically in real life JavaScript developers will often fight to death over whether or not to use semicolons.
Let, is not the only way to define a variable another common option is Const, which is used for variables that cannot be reassigned later, but the original way to declare a variable is Var. I would recommend ignoring its existence altogether although you will find it out in the wild.
The reason we have so many different ways to Define variables has to do with the lexical environment which determines where variables work and where they don't. There's a global scope which means the variable will be available everywhere. However, if we define a variable inside a function, it then becomes local to that function and cannot be used outside of it.
Finally, if you have a statement like an If condition, variables can be scoped inside the braces or block unless you use Var for that variable which is not block scope in which case it will be hoisted up into the local scope for that function and trust me you don't want that weirdness in your life.
When the function keyword is used by itself, it's called a function definition or statement. Functions are one of the main building blocks in JavaScript and they work by taking input or arguments and then optionally returning a value that can be used somewhere else.
Functions are just objects which means they can also be used as Expressions allowing them to be used as variables or to construct Higher-order functions, where a function is used as an argument or a return value.
Functions can also be nested to create a closure that encapsulates data and logic from the rest of the program. Normally, when you call a function that has a variable with a primitive value, it's stored on the call stack, which is the browser's short-term memory.
However, when you create a closure, the inner function can still access variables in the outer function even after the initial function call. That happens because JavaScript automatically stores the data in the outer function in the Heap memory which will persist between function calls.
You'll rarely have to think about that as a developer but what you're more likely to run into is This. It's a keyword that references an object based on how a function is called. When called from the global scope, it references the window object in the browser. However, if that same function Is attached to an object and called by that object, this will be a reference to that object.
You can manually bind a function to some other objects using the bind method. This can be rather confusing but modern JavaScript has another way of defining functions using the arrow syntax. Arrow functions don't have their own this value and they're always Anonymous, which makes them ideal for function Expressions.
Now one last thing you need to know about functions is that when passing arguments, a primitive like a number is passed by value which means a copy is created of the original variable. However, if the argument is an object that means it's stored in the Heap and passed by reference. That means multiple parts of the code might be mutating the same object, speaking of which, let's talk about objects.
The easiest way to define one is with the object literal syntax using braces. But there's also an object type that can be created with a constructor using the new keyword. An object contains a collection of key-value pairs or properties and values. What's interesting is that objects can inherit properties from each other thanks to a mechanism called the Prototype chain. Every object has a private property that links to exactly one prototype.
This differs from class-based inheritance found in many other languages because we're dealing with real objects that take out memory as opposed to abstract classes in your code. Now what's confusing is that Javascript supports object-oriented programming with a class keyword. However, classes are just syntactic sugar for prototypal inheritance and objects.
A class can define a constructor which is a function that's called when the object is first created. It can also have properties and optionally create Getters and Setters to access them and it more easily encapsulates functions by organizing them as methods on an object instance or making them Global to the class name with the static keyword.
In addition to objects, JavaScript has a bunch of built-in data structures like an array for holding a dynamic collection of indexed items, a Set to hold a collection of unique items or a Map which also holds a key-value pair like an object but can be more easily looped over along with a variety of other features. Now, you should also know at this point that JavaScript is garbage-collected which means it will automatically de-allocate objects from memory when they're no longer referenced in your code.
However, when you have a map, all the properties will always be referenced. If that's not optimal, there's a weak map and weak set that contain properties that can be Garbage-collected to reduce memory usage.
Now that we have a basic idea of what JavaScript looks like, let's talk about one of its most interesting features which is its Non-blocking Event Loop. Normally, when you write code in a script, it's executed synchronously line by line which means the next line can't start until the previous line finishes.
With an event Loop, we can write asynchronous code in JavaScript that runs in a separate thread pool while the rest of the application continues to execute. This is important because modern websites often have multiple things going on at the same time, but they only have access to a single thread for computing called the main thread.
Without asynchronous code, it would be impossible to multitask. An easy way to demonstrate this is with Set timeout which takes a function as an argument but won't call that function until X number of milliseconds have elapsed. This is called a Callback function because it gets enqueued in the event Loop, and will need to be called back later when it's ready to execute on the main thread.
Callback functions are very common, but when they're overused and become too deeply nested, it leads to a situation called callback hell. Luckily there are better ways to write async code like a promise.
A promise is a wrapper for a value that's unknown right now but will resolve to a value in the future. Like maybe a call to a third-party API that resolves to some data, if something goes wrong, the promise can Reject to raise an error.
Now the consumer of the promise can use methods like then and catch to handle these two possible outcomes or better yet you can define an async function that will automatically return a promise then in the body of the function we can pause its execution using the await keyword to wait for other promises to resolve.
This results in a nice readable code however to implement error handling you'll want to wrap this code in a Try-catch block.
As your code grows in complexity it won't all fit in a single file. Luckily we can use modules to share code between files. By default, all the code in a file or module is private to that file. However, if there is some code we want to use elsewhere like a function, we can make it a default export. That allows us to go into a different file and use an import statement to use the function there as well.
It's also possible to export multiple values from a single file and then import them by name (named import) in the other file. But often what you'll do in JavaScript land is use code written by an entirely different developer.
The largest JavaScript package manager is NPM. When you install a package from NPM, it downloads its code into the node modules folder in your project. It also provides a package JSON file that will list out all the dependencies used in your project.
Let's assume we're building a website on the web, the code will run in the browser which is based on the Document object model (DOM) where the UI is represented as a tree of HTML elements or nodes.
The browser provides APIs to interact with these nodes, with the most important object being the Document. The document allows us to grab an individual dual HTML element using a method called Query Selector.
It takes a CSS selector as an argument and will find the HTML element that has the same class name, Id, or tag name. It returns an instance of the element class, which itself has a variety of properties and methods you get information about it or change its behavior.
In addition, we can grab multiple elements at the same time using query selector all. Most importantly we can listen to events that happen to it. Like when a button is clicked with an add event listener we can assign a function that will be called whenever that event takes place.
Much of web development revolves around listening to events and updating the UI accordingly However, one thing that many developers dislike about vanilla JavaScript is that it results in imperative code where the UI is mutated directly. Many developers now use front-end Frameworks that produce declarative code where the UI is a function of its input data.
These libraries encapsulate JavaScript, HTML, and CSS into components, which are then used together to form a component tree to represent the UI. Most importantly inside a component data is reactive. It can be bound (data binding) from the JavaScript directly to the HTML. That means anytime data changes the UI will be updated automatically.
Now after you build a complete Javascript app, you'll need to take all of your JavaScript files and combine them into a single bundle that can be used by the browser to handle this process efficiently.
You'll need a tool called a module bundler like Vite or Webpack. One problem though is that sometimes this Javascript file can get too big, which affects the page load performance, and this can be measured by the network waterfall in your browser Dev tools. Luckily, it's possible to split this JavaScript bundle into multiple files, to achieve that, use dynamic Imports in your code to only import that JavaScript when it becomes needed.
Now JavaScript doesn't just run in the browser but also on the server. Node.js is the most popular runtime and you can execute JavaScript code at any time using the node command.
This opens the door to Frameworks like Electron which combine Nodejs with a browser to create full-stack desktop apps with JavaScript, or IOS and Android apps with react native (cross-platform).
At this point, you've got 99 problems and JavaScript is every single one of them. If you want to make life easier use a tool like Typescript or Eslint that does static analysis to improve your code quality.
Conclusion
Congratulations on reaching the end of JavaScript 101. In summary, mastering JavaScript's core concepts, from variables to events, is pivotal for creating dynamic web applications. This foundational understanding boosts coding proficiency and fosters creativity and innovation. Navigating these fundamentals is key to unlocking the full potential of JavaScript in the dynamic realm of web development.



