← Back to engineering journal

JavaScript Gets Weird When You Go Deeper.

JavaScript looks simple until references, closures, asynchronous execution, promises and the event loop start behaving in ways that are not obvious from the code you wrote.

JavaScript is easy until you stop asking what the code does and start asking why it behaves that way.

At first, JavaScript feels almost too friendly.

Variables hold values. Functions execute. Arrays contain data. Objects contain properties.

Then one day you change an object and another variable changes too.

A function remembers a variable that should have disappeared.

A timeout does not execute when you expected it to.

A promise appears to run before something that was written before it.

That is the point where JavaScript becomes interesting.

FIELD NOTE 006

JavaScript becomes much easier when you stop memorizing behavior and start understanding the execution model behind it.

01 / THE BEGINNING

Values are not always as simple as they look.

Consider a primitive value.

PRIMITIVE JAVASCRIPT
let a = 10;
let b = a;

b = 20;

console.log(a);
console.log(b);

The result is straightforward because primitive values such as numbers are copied as values.

Objects introduce a different mental model.

02 / REFERENCES

Two variables can point to the same object.

REFERENCE JAVASCRIPT
const user = {
  name: "Prince"
};

const anotherUser = user;

anotherUser.name = "Alex";

console.log(user.name);

The surprising part is that changing `anotherUser` also changes what `user` sees.

The reason is not that JavaScript randomly copied the object. Both variables refer to the same object.

MENTAL MODEL Two labels can point to the same object in memory.

Understanding this becomes extremely important when working with React state, arrays, objects and immutable updates.

03 / CLOSURES

Then functions start remembering things.

A closure is one of those concepts that sounds complicated until you see it.

CLOSURE JAVASCRIPT
function createCounter() {

  let count = 0;

  return function () {
    count++;
    return count;
  };

}

const counter = createCounter();

console.log(counter());
console.log(counter());
console.log(counter());

The `createCounter` function has finished executing, but the returned function can still access `count`.

That remembered access is the important part.

Closures appear everywhere in modern JavaScript: callbacks, event handlers, timers, modules and React hooks.

Once closures make sense, many JavaScript patterns stop looking like magic.

04 / ASYNC

JavaScript does not simply execute everything from top to bottom.

Consider this:

ASYNC JAVASCRIPT
console.log("A");

setTimeout(() => {
  console.log("B");
}, 0);

console.log("C");

The output is:

OUTPUT ORDER
A
C
B

The zero-millisecond timeout does not mean “execute immediately.” It means the callback can be scheduled after the current synchronous work is completed.

05 / EVENT LOOP

The event loop is where the weirdness starts making sense.

JavaScript executes synchronous code using its call stack. Asynchronous operations can be handled by the surrounding runtime, and callbacks are scheduled to run when the relevant execution conditions are met.

01 CALL STACK Runs code
02 QUEUE Waits
03 EVENT LOOP Schedules

The browser and JavaScript runtime provide mechanisms around the language that allow timers, network requests and other asynchronous operations to work without blocking the entire application.

This is one of the reasons JavaScript can handle large numbers of I/O-oriented operations while maintaining a responsive interface.

06 / PROMISES

Promises make asynchronous results easier to compose.

A promise represents an eventual result of an asynchronous operation.

PROMISE JAVASCRIPT
fetch("/api/products")
  .then(response => response.json())
  .then(products => {
    console.log(products);
  })
  .catch(error => {
    console.error(error);
  });

Modern JavaScript often uses `async` and `await` because it can make asynchronous control flow easier to read.

ASYNC / AWAIT JAVASCRIPT
async function loadProducts() {

  try {

    const response = await fetch(
      "/api/products"
    );

    const products = await response.json();

    console.log(products);

  } catch (error) {

    console.error(error);

  }

}

The important thing is not choosing one syntax because it looks modern. The important thing is understanding that the operation is asynchronous and that the result is not available immediately.

07 / MISTAKES

The mistakes that made JavaScript finally click.

01 Treating objects like copied values

Understanding references prevents many unexpected mutation bugs.

02 Thinking async means “later”

Asynchronous behavior depends on the runtime and scheduling mechanisms, not simply an imaginary timeline.

03 Ignoring closures

Closures explain why callbacks and functions can continue accessing variables from an outer scope.

04 Memorizing outputs

Memorizing console output helps with one question. Understanding execution helps with the next hundred.

08 / LESSONS

JavaScript becomes easier when you build a mental model.

I used to approach difficult JavaScript behavior by trying to remember the answer.

That works until the next slightly different example appears.

A better approach is to ask:

QUESTIONS MENTAL MODEL
What value is this?

Is this a reference?

Which scope owns this variable?

Is this code synchronous?

When is this callback scheduled?

What is currently on the call stack?

Those questions turn confusing behavior into something you can reason about.

09 / FINAL THOUGHT

You don't need to memorize JavaScript. You need to understand it.

The language has many details, but the deeper concepts connect.

References explain mutation. Scope explains closures. Closures explain many callback patterns. The runtime explains asynchronous behavior. Promises provide a structured way to work with asynchronous results.

Once those pieces start connecting, JavaScript stops feeling random.

FIELD NOTE / 006 Don't memorize
the weird behavior.
Understand why
it happens.
PREVIOUS ARTICLE ← The Day My Database Became the Problem NEXT ARTICLE Java vs JavaScript: Stop Comparing Them →

Have something
worth building?

Need a developer or want to discuss an interesting project? Let's build something useful.

Contact Prince →