Old CMS excavation site: buried JavaScript patterns
Remember the time before arrow functions, when var was everywhere and JavaScript code looked noticeably different from what we write today? Recently, I stumbled upon some old code in a custom CMS interface that looked genuinely ancient, and it made me think of all those obscure JavaScript patterns that used to be extremely common. The former self Losing the execution context was a very common problem back then. JavaScript's runtime binding of this has always been a source of confusion. Instead of explicitly binding callbacks, developers often captured the surrounding this in a variable - usually called self or that. var self = this button.addEventListener('click', function () { self.openModal() }) With arrow functions the variable declaration can be completely skipped: button.addEventListener('click', () => { this.openModal() }) Arrow functions inherit this from the surrounding lexical scope, so the extra variable is no longer necessary. Into the void The void operator is one of JavaScript's oldest and more obscure features. It isn't obsolete - there are still legitimate uses for it, but most developers rarely encounter it today. void evaluates an expression and always produces undefined. That’s not something that one will use on a daily basis now, but in the legacy-web era you could frequently spot it in href attribute: Do something Anchors were frequently used as generic clickable controls, even when clicking them wasn't supposed to navigate anywhere. javascript:void(0) provided a convenient fake target: the JavaScript expression runs, evaluates to undefined, and the browser stays on the current page. As semantic elements became the preferred choice for actions, and event handling practices improved, this pattern gradually disappeared. Today, javascript:void(0) feels like a small fossil from an earlier web. Callback hell Of all the indescribable horrors lurking in old JavaScript codebases, callback hell might be the most terrifying. Even after putting the Lovecraftian imagery aside, we can probably agree that callback pyramids aren't something to be nostalgic about. The cognitive cost rises quickly with every additional level of nesting. Promises first gave us a way to flatten asynchronous chains, and with async/await arriving in ES2017, the fog finally started to lift. And instead of this madness: getUser(id, function (user) { getPosts(user.id, function (posts) { getComments(posts[0].id, function (comments) { saveComments(comments, function () { renderPage() }) }) }) }) We eventually made our way toward a brighter future: const user = await getUser(id) const posts = await getPosts(user.id) const comments = await getComments(posts[0].id) await saveComments(comments) renderPage() Private little scope Before ES6 modules were introduced, Immediately Invoked Function Expressions (IIFEs) were a common way to encapsulate logic and avoid leaking variables into the global scope. (function () { var a = 10 var b = 20 console.log(a + b) })() console.log(a) // ReferenceError Back when var was everywhere, wrapping code in a function was one of the easiest ways to create a private scope. Variables declared inside the IIFE stayed inside it instead of becoming part of the surrounding scope. ES6 changed this quite a bit. Modules provide their own scope, while let and const introduced block scoping: { const a = 10 const b = 20 console.log(a + b) } IIFEs never became something to frown upon, they simply became a more situational tool. An async IIFE, for example, can still be useful when you need await in a context where top-level await isn't available: ;(async () => { const data = await loadData() console.log(data) })() I’m sure I’ve missed a few fossils here. What JavaScript pattern used to be everywhere in your codebase, but now feels almost extinct?
This is a summary aggregated from Dev.to. Read the complete article on the original site:
Read full article at Dev.to