Javascript - With great power comes great responsibility
First some Basic stuff
Javascript is a loosly typed language, which is JIT(Just-In-Time) compiled by a JS Engine like Chromes V8. This means we do not specify a type to a variable until we INITIALIZE a variable.
If you are learning more about javascript, the interpretation and compilation, you have probably come across the term "hoisting" (or maybe not). As you can see in the cover photo above, what is happening before the code is executed, is that all declarations are moved to the top of the scope (function scope in this case). But let me tell you here why this not just a weird behaviour that induces alot of potential side-effects, but when used properly can be very powerful.
Why is it Bad?
Well first of all, the most obvious reason this is bad is because its super hard to read for the person trying to reason about the coding and also the loose typing leads to all kinds of misinterpretation by the developer.
Less obvious but even more painful to troubleshoot are side-effects that can happen due to function scope or type coercion. In JS you might have seen things like this happening before:
true + true // -> 2
true - true // -> false
typeof Number // -> NaN (Not a Number)
Most developers coming from typed programming languages are used to the concepts of block scope and have a strict compiler checking for any type errors during compiletime. Since Javascript is JIT Compiled these errors appear during runtime and can be extremly frustrating for the enduser.
There are native implementations like let and const which declare variables in block scope but also frameworks like typescript that add a precompilation step to your javascript and implement a more controlled version of typing.
Why it is also extremly cool
It becomes really cool once we are introduced to the concept of closure. If functions are enclosed in one another they also hold reference to the lexical environment (the state "around" them). In other Words:
a closure gives you access to an outer functions scope from an inner function
Along with the loose typing you can implement polymorphism in a more expressive way. The complexity of managing this though, grows with the size of your application.
Here is an example utilizing prototypal inheritance with closure:
//prototype on Window
Window.prototype.handleResize = function(resizer, tick){
var worker = { isWorking: false, listeners: []};
this.addEventListener('resize', function(event){
if(worker.isWorking) return null;
worker.isWorking = true;
setTimeout(function(){
worker.listeners.forEach(function(listener){ listener(resizer(event)); })
worker.isWorking = false;
}.bind(this), tick || 100);
}.bind(this));
return {
dispatchTo: function(func){
var index = worker.listeners.push(func) - 1;
return function(){
worker.listeners.splice(index, 1)
}
}
}
};
Here I am introducing a way to perform various actions on window resize:
var getWidth = function(e){ return e.target.innerWidth; }
var resizeWidth = window.handleResize(getWidth) //(default: only perform every 100 ms) -> due to performance
and dispatching this event to a handler:
var logWidth = function(width){
console.log("my new width is: " + width);
}
var listener = resizeWidth.dispatchTo(logWidth)
While we are resizing a window we will broadcast an event that outputs the width which will then be logged every 100ms (tick parameter) to avoid cluttering (debounce). We achieve a high amount of expressivness in the implementation.
Key Takeaway
With great power comes great responsibility: Be responsible when coding and watch out for nasty side-effects. Discipline yourself with concepts like immutability and master the powers that lie within javascript.