Requiem for a Query

Issue #215.Invalid Date.2 Minute read.

Today’s issue: The return of ShadowDOM666, building garbage collectors for fun, and how the Scatman can help you get comfortable with the command line.

Welcome to #215.

Eyeballs logo

The Main Thing

Goblin from Harry Potter reading a book.

Read me the adventures of XMLHttpRequest again, papa

Requiem for a Query

jQuery turns 17 years old next week, which means that the old Dollar-Tree Library will finally be able to see a R-rated movie without having to sneak in the back (like I did when I saw Tropic Thunder for the first time).

And even though most of us haven’t sipped that JQ juice for a while now, you could easily argue that it’s the 🐐 JS framework — because it emerged at a time when JavaScript needed it most. Let me explain.

The web was a very different place when jQuery came on the scene in 2006. And by “different”, I mostly mean worse.

Chrome was still two years away from launching, and the dominant browsers at the time (Firefox, IE, and Safari) all used different browser engines that implemented JavaScript and the DOM in slightly different ways. This forced developers to spend a disgusting amount of time making sure their sites were “cross-browser compatible”, and just reading that phrase in 2023 has probably given every developer over 40 a painful series of flashbacks. (Sorry for the lack of trigger warning.)

To make matters worse, vanilla JavaScript was nowhere close to what it is today. We were still stuck on ECMAScript 3, and ES4 was failing to reach consensus among the TC39 committee. Fundamental tasks that we now take for granted, like fetching async data and basic DOM manipulation, required lots of complex and verbose code.

Out of this darkness, our hero emerged.

jQuery normalized browser behavior by abstracting away the differences between browser engines, and gave us one unified approach to interact with the DOM on every browser. It also provided much simpler and more expressive syntax than vanilla JS for selecting and manipulating DOM elements — $ ftw.

These abstractions didn’t actually “fix” any of the web’s problems, but they provided a helpful band-aid that shielded developers from the most painful parts of web dev at the time.

But band-aids are meant to be removed, eventually. And in the mid 2010s—as vanilla JavaScript became increasingly robust with ES5 and ES6, and web browsers united around common standards—we stopped needing jQuery the way we once did.

It was like Police Commissioner James Gordon had finally gotten the crime in Gotham under control, so the city didn’t really need Batman anymore. Thus, jQuery started to give way to the next generation of frameworks that were helping to abstract away new and different problems.

But here’s an interesting question to consider: What would’ve happened to jQuery if it had been incubated within a big tech company like React or Angular?

We obviously can’t know for sure, but React provides an interesting counter-example. Though the core principles have stayed mostly the same, React itself has changed dramatically over the last 10 years.

If John Resig and a “jQuery Core Team” were being paid FAANG-bucks to work on the library full time, would they have tried to innovate more and turn jQuery into something it was never intended to be in order to justify their salaries? They definitely would’ve had powerful incentives to do so. And who knows, maybe we’d all still be sipping that JQ juice today like it was still 2009.

Bottom Line: Happy birthday, king.


Axiom logo

Our Friends
(With Benefits)

Joey Chestnut standing with a plate full of hotdogs

Axiom getting ready to ingest all the data.

Axiom gives you all your event data, all in one place

That’s because their unique datastore makes storage significantly more affordable than most legacy logging platforms. (Check out the docs to see how it works.)

This means you can store all your data, instead of just a sampling, and still save money vs. other logging solutions. It’s basically black magic.

Here’s how that makes your life easier:

  • Query all your data quickly and simply, without having to move data from cold/archive to “hot.”

  • Never worry about deleting any of your data from any source ever again — logs, events, frontend, backends, etc.

  • Get better insights, quicker debugging, and less hassle for your team.

Check out the free-forever plan to see how much time (and money) you could save.

Spot the Bug logo

Spot the Bug

Sponsored by Secureframe

Getting SOC 2 compliant doesn’t have to be painful. Check out Secureframe’s free personalized demo to see how they can help you do it in just a few weeks.

function reduceLanguages (str, lang, i) {
  if (i === this.languages.length - 1) {
    return `${str} and ${lang}.`;

  return `${str} ${lang},`;

const user = {
  name: "Tyler",
  age: 27,
  languages: ["JavaScript", "Ruby", "Python"],
  greet() {
    const hello = `Hello, my name is ${} and I know`;

    const langs = this.languages.reduce(reduceLanguages, "");

    return hello + langs;

const greeting = user.greet()

Cool Bits logo

Cool Bits

  1. Steve Krouse wrote about how he Fought JavaScript, and JavaScript won by trying to add too many custom features to JS while building Val Town. The Clash tried to warn us.

  2. Umair Nadeem and Rich Hong wrote about How Dropbox reduced the size of its JS bundles by 33% by expanding their adoption of Rollup.

  3. Multi is a game-changing new way to work on code together with your team. Their free MacOS app lets you point, draw, and take control of your teammates’ apps, so it feels like you’re actually working together in the same room. Click here to try it out and skip their (very long) waitlist. [sponsored]

  4. Node 20.6.0 will include built-in support for .env files.

  5. Corey Quinn ran the numbers and figured out what Amazon’s AWS Bill would’ve been for Prime Day 2023 if they had to pay commercial prices. Spoiler: It’s over $100m. They should really think about switching to Cloudflare.

  6. Simon MacDonald (from the Enhance team) wrote about Why Enhance doesn’t use the Shadow DOM by default. But he failed to mention how ShadowDOM666 was my Halo 3 gamertag in high school, when I was promoted all the way to the rank of Brigadier General. Rude.

  7. Supabase just launched a bunch of stuff like a Postgres language server, better testing and observability tools, and type generators.

  8. Clayton Ramsey wrote about how he Built a garbage collector for a language that doesn’t need one. Because that’s what you do for fun when you’re getting a Computer Science PhD like Clayton. Pretty impressive, but how are your skills with needlers and sticky grenades?

  9. Thibault Le Ouay Ducasse wrote about Why their team migrated from PlanetScale to Turso.

  10. Julia Evans wrote a post called What helps people get comfortable on the command line? I always assumed it was the same thing that helps people get comfortable on the conga line — a couple whiskey sours and a heavy dose of the Scatman.

Spot the Bug logo

Spot the Bug: Solution

If you run the original code, you’ll get “Uncaught TypeError: Cannot read properties of undefined (reading ‘length’)“. The only place we’re using .length is inside our reduceLanguages function, so we can infer that this.languages is undefined and we’re incorrectly using the this keyword.

Whenever you’re trying to figure out what the this keyword is referencing, you need to look at where the function using the this keyword is being invoked. In our example, we don’t know since it’s in the implementation of reduce. To make sure reduceLanguages gets invoked in the right context and therefor has the correct this binding, we have two choices. First, the old school way - using .bind.

greet() {
  const hello = `Hello, my name is ${} and I know`;

  const langs = this.languages.reduce(reduceLanguages.bind(this), "");

  return hello + langs;

Second, the better way - using an arrow function.

greet() {
  const hello = `Hello, my name is ${} and I know`;

  const langs = this.languages.reduce((str, lang, i) => {
    if (i === this.languages.length - 1) {
      return `${str} and ${lang}.`;

    return `${str} ${lang},`;   
  }, "");

  return hello + langs;

The reason this works is because with arrow functions, this is determined lexically. Arrow functions don’t have their own this. Instead, just like with variable lookups, the JavaScript interpreter will look to the enclosing (parent) scope to determine what this should reference.

Want to read more? Check out Understanding the “this” keyword, call, apply, and bind in JavaScript.