JavaScript lawmaking on an LCD monitor
Photo by Pankaj Patel on Unsplash

What’s what and where

JavaScript is scrutinizingly famous for some of it’s weird behaviours virtually variables. This goal of this vendible is to make it so that you will never be surprised by the value of this or why a variable seems to not transpiration when you set it somewhere else.

TL;DR: “use strict” is good, JavaScript modules better. Never use var , use const where possible, use let sparingly. Use blocks to telescopic the impact of variables. The IIFE is a pretty tomfool pattern. Combined with a good linter you will write lawmaking which should hopefully work first time. Or don’t, I'm not a cop.

Before “use strict”, const and let

In the whence when JavaScript was only on the web not the behemoth of a language it is today. Variables were specified with var and if they were specified on at the top level of a script (i.e. not in a function) they were moreover exposed on the global telescopic and the window object.

This made using multiple scripts that work together easy since they all shared a scope, but when programs got increasingly complicated it left lots of variables cluttering up the global telescopic leading to weird and frustrating bugs.

There would be weird behaviour like declaring the same variable twice would not throw an error! So if two scripts used the same variable, or you unwittingly requite two variables the same name, the first gets overwritten by the second leading to some nonflexible to find bugs.

In the early days you could plane get yonder with forgetting to declare a variable entirely. This would have the unfortunate effect of unchangingly putting the variable in the global telescopic and on the window object plane if it was first used in a function. This behaviour is very bad.

test0 = 0;
var test1 = 1;
function runMe() {
test4 = 4
var test5 = 5;
}
runMe();
  • test0 and test1 is in the global telescopic and exposed as window.test0 and window.test1
  • test4 is moreover on the global telescopic and exposed as window.test4
  • test5 is undefined and undeclared

Interestingly if you use const and let at the top level here they are not placed on the window object but are still misogynist on global scope.

Because variables in functions are constrained to the telescopic of that function a worldwide pattern was to contain your script you wanted to not be exposed in an Immediately Invoked Function Expression known as an IIFE (pronounced iffy). A popular pattern most JavaScript bundlers use is to place the whole file inside an IIFE.

(function () {
var cannotEscape = 'I am not supposed outside of this function';
}());

Closures

Functions in JavaScript still have wangle to all the specified variables in it’s parent’s telescopic and it’s grandparent scope, all the way up to the global scope. This behaviour is known as closures. It’s weightier practice to limit the telescopic of a function to only the variables it needs to exist. If a function doesn’t need wangle to the state of it’s parent closure then it’s usually weightier to move it up to to an IIFE in the top level to reduce it’s footprint and aid garbage collection.

As all specified variables in the telescopic of a function cannot be garbage placid as long as the function itself is still available, this can lead to memory leaks.

For this reason defining functions inside loops is a bad idea considering each time it’s created it is a unshared function is created which will sooner need to be collected, which can rationalization slowness. Plane worse, if these functions aren’t worldly-wise to be garbage placid considering they are referenced somewhere else then you have potentially created a memory leak!

MDN’s vendible on closures is interesting if this vendible doesn’t satisfy

“use strict”

The strict mode of JavaScript was designed to make JavaScript increasingly reliable by throwing errors for mistakes which previously would’ve been been executed in a way that may have been unexpected to the developer. I.e. forgetting to declare a variable and it unwittingly ending up in the global telescopic making weird things happen.

To turn on script mode have the first statement in the script be just the string "use strict"; it can moreover be invoked on a function level too.

I won’t go into detail on the effects of strict mode the, strict mode vendible on MDN covers it well.

The main thing it introduced for us is that trying to use undeclared variables now throw errors.

Variables specified with var still end up on the window object and in global scope. Variables specified with const and let still are misogynist globally but are not on the window object.

Variables with the same name supposed multiple times with var still overwrite each other and not throw an error.

Hoisting

Variables specified with with var are supposed for the whole closure no matter where in the closure they were defined. The declaration is invisibly “hoisted” to the top. Think of it like hoisting a flag where the flag gets pulled to the top.

The pursuit function does not throw an error.

function () {
"use strict"
console.log(p); // undefined
var p = 2;
}

Because this is what is really happening:

function () {
"use strict"
var p;
console.log(p); //undefined
p = 2;
}

This is a behaviour you need to squint out for when you are using loops. Where the variable is referenced outside of the context of the loop.

for (var i=0;i<10;i  ) {
var printMe = "Test " i;
setTimeout(function () {console.log(printMe)}, 100)
}
// Prints "Test 9", 10 times

Because it’s effectively, updating printMe 10 times then printing the final value of printMe ten times. If you transpiration the var to a let this bug gets stock-still immediately considering let is woodcut scoped and then each function in the loop has it’s own reprinting of printMe which it can print.

for (var i=0;i<10;i  ) {
let printMe = "Test " i;
setTimeout(function () {console.log(printMe)}, 100)
}

The hoisting behaviour is not obvious to many people new to the language. So it’s weightier to explicitly declare your variables at the top of the function telescopic so you can be well-spoken what exactly is happening.

function doSomething() {
var i=0;
var output="";
var intermediate;
  for (i=0;i<10;i  ) {
intermediate = '<span>' i '</span>\\n'
output = intermediate;
}
}

Functions

There are many ways to pinpoint a function. A named function is specified as if you used var

if (true) {
function myFunc() {
  }
}
myFunc // function
window.myFunc // function

is equivalent to:

var myFunc;
if (true) {
myFunc = function () {}
}
myFunc // function
window.myFunc // function

The exception is named IIFEs which are not:

(function test() {
console.log(test) // function
}())
test // ReferenceError: test is not defined

Named IIFEs are handy for if you want to run something once then reuse the function for other things. Be shielding to stave recursion though!

Const and Let

These slightly newer ways of describing variables have some really nice properties, that fix most of the weirdness in var. A big difference is that they are never automatically prescribed to the window object but they do remain misogynist in the global telescopic if they are defined there.

const and let are scoped to their containing block, i.e. the nearest curly twosome the {} symbols. This makes them very useful for having variables which are only used in a single loop or if statement.

const a = true;
if (a) {
const b=2;
console.log(b); // 2
}
console.log(b); // throws an error considering b is not defined.

If you want to group some logic and it’s associated variables together, now instead of using an IIFE you can use a woodcut statement:

{
const a = 2;
console.log(a);
}
// labeled woodcut statement
myBlock: {
const a = 2;
console.log(a);
}

If you try to pinpoint a variable with const and let twice in the same telescopic an error will be thrown preventing weird bugs from unwittingly creating two variables with the same name but child blocks can redeclare the variable and will replace it for that block only.

const a=2;
{
const a=3;
{
const a=4;
console.log(a); // 4
}
}
console.log(a); // 2

Another difference from var is that const and let are never hoisted. So if you try to use them surpassing they are specified you just get a syntax error.

(function () {
"use strict"
console.log(p); // Error, Cannot wangle 'p' surpassing initialization
const p = 2;
}())

This moreover applies if you are going to redefine a variable in the parent lexical telescopic and try using it surpassing if it is specified an error is thrown.

const a=2;
{
const a=3;
{
console.log(a); // Error, Cannot wangle 'a' surpassing initialization
const a=4;
console.log(a);
}
}
console.log(a);

I hope this section has emphasised how useful using let and const are to writing wipe lawmaking which minimises the leaking of variables to the outer scopes.

Const vs Let

We just talked well-nigh how const and let are similar. but how do they differ?

Essentially let can be redefined, const cannot.

const is very useful for lamister unwittingly replacing a variables value. Which is incredibly useful in an untyped language like JavaScript. When I write lawmaking I use const by default and only use let if I have a need to replace the value. If I use a let then I know that I need to be careful.

JavaScript objects prescribed to a const can still be modified but you cannot replace the object with flipside object. To prevent modification you need to use Seal and Freeze.

They are moreover useful in loops, use const for “for of” loops or “for in” loops.

for (const el of document.body.children) console.log(el);

Use let for standard for loops considering the value of the variable is modified each iteration.

for (let i=0;i<10;i  ) console.log(i);

The woodcut scoping behaviour of const and let includes try{}catch(e){} blocks. This is one situation where using let is important. Since let and const used in these blocks can’t be accessed from outside of them.

let answer;
try {
wordplay = canThrow();
} reservation (e) {
wordplay = null;
}
if (answer !== null) {
// huzzah
}

Global Behaviour in JavaScript Modules

You can use JavaScript modules by subtracting them to the DOM with type="module" e.g.

<script src="myscript.js" type="module"></script>

Modules are unchangingly strict mode. You don’t need to declare strict mode and you can’t not have strict mode.

Unlike with regular script tags variables specified with var do not end up on the window object. In wing no variables are overly exposed in the global telescopic unless you manually assign them to the window object.

this

An scrutinizingly philosophical question in JavaScript is “Oh so you know JavaScript, then what is this?”

The behaviour of this can seem troublemaking but there are various tools which make it easier to deal with.

At the top level this is the global object. The global object can be accessed any where in your lawmaking using globalThis (globalThis on MDN)

"use strict"
this // Window {window: Window, self: Window, document: document, name: '', location: Location, …}
this === globalThis // true

In Web Workers this is the global telescopic for the worker which is variegated from the window object, which it cannot access. If you want to communicate when to the window you need to use things like postMessage.

"use strict"
this // DedicatedWorkerGlobalScope {name: '', onmessage: null, onmessageerror: null, cancelAnimationFrame: ƒ, close: ƒ, …}
this === globalThis // true

The value of this in a function changes depending on how a function is called. If you undeniability a function on an object then this is set to that object. If you run the same function independently then in strict mode it is undefined.

If you run an self-sustaining function when not in not strict mode this is set to the global object which can lead to all sorts of nonflexible to spot errors!!

"use strict"
const o = {
b() {return this}
}
console.log(o.b()) // {b: function () }
const t=o.b;
console.log(t()) // undefined

Certain methods which take functions as callbacks will set this to be something else. Some examples are setTimeout and setInterval which will set this to the global object.

"use strict"
const o = {
b() {
setTimeout(function () {
console.log(this); // window
},10)
}
}
o.b();

and element.addEventListener callbacks where this will be the element the event listener was set to. In the example unelevated with click set on the body, this is set to the soul plane if the very element I clicked on was a sawed-off and the event bubbled up.

document.body.addEventListener('click', function (e) {
console.log(this); // <body>
console.log(e.target); // <button>
});

You can manually set this in a few ways. Starting from my most used to least used:

Fat thunderstroke functions, inherit this from where they were defined.

These are incredibly useful when you are setting events in a matriculation where the accessing the matriculation from inside the event listener function is much increasingly useful than the element that was clicked.

"use strict"
const o = {
a() {
this.b = ()=>this;
}
}
o.a();
console.log(o.b()) // {b: function () }
const t=o.b;
console.log(t()) // {b: function () }

function.bind( newThisElement ), this creates a new function where this is stock-still to a particular value, plane if tabbed from functions like event listeners. If you need to use removeEventListener this is really useful considering you can pinpoint a function unseat to where you need it and can then remove it later using the same function definition.

"use strict"
const o = {a:2};
function f() {return this.a};
const f1 = f.bind(o);
f1() // 2

function.call() and function.apply(), these similar functions indulge you to run a function once where the first treatise is the new this value and the other arguments are the arguments to use on the function. Read the MDN vendible to know more, I don’t use these often but are handy to know. function.apply and function.call on MDN

Classes

The behaviour for this in classes is the same as that for objects so I won’t go over it again. Instead I will squint at where you can store variables in classes. You can set public variables on this as you would expect.

You can declare private variables when you pinpoint the class. They can only be accessed from functions specified in the matriculation description. Trying to wangle them or add functions to wangle them later results Syntax errors. So they really are private! Private matriculation fields on MDN

class A {
#myPrivate
constructor(){
this.myPublic = 'Butts lol'
this.#myPrivate = 'Hello World'
}
get myThing() { return this.#myPrivate} // Optional getter to wangle the private field
set myThing(val) { this.#myPrivate = val } // Optional setter to set the private field
}
const test = new A()
test.#myPrivate // Syntax Error
test.hackTheGibson = function () {return this.#myPrivate} // Syntax error too!
test.hackTheGibson2 = function () {return eval('this.#myPrivate')}
test.hackTheGibson2() // Syntax error

You can use closures to emulate private data by having data which is only misogynist in a closure in a function.

"use strict"
const o = {};
(function () {
let myPrivate = 'hello world';
Object.defineProperty(o, 'myThing', {
get: function () {return myPrivate},
set: function (val) {myPrivate = val;}
});
}())

Patterns like this with closures are really fantastic and reveal some of the subconscious powers of JavaScript. This book, JavaScript patterns is old but short and really good for seeing some of these patterns for taking wholesomeness of closures to create various features which when then weren’t in the language at all. I strongly recommend it for those who want to get an in depth understanding of how JavaScript works at it’s core.

JavaScript Patterns: Build Largest Applications with Coding and Design Patterns


JavaScript telescopic and closures was originally published in Samsung Internet Developers on Medium, where people are standing the conversation by highlighting and responding to this story.