Algorithm|Javascript Algorithm

JavaScript Prototype Methods: The Hidden Cost Behind Convenience

4
JavaScript Prototype Methods: The Hidden Cost Behind Convenience

Chris was implementing a 'Queue' data structure.

A Queue is a First-In-First-Out (FIFO) structure, much like a waiting line where the first person to arrive is the first to leave.

"JavaScript arrays are the best. I can just push when inserting and shift when removing. Done!"

javascript
// ❌ Chris's Queue Implementation
const queue = [];

// Enqueue 100,000 items
for (let i = 0; i < 100000; i++) {
  queue.push(i);
}

// Dequeue 100,000 items
while (queue.length > 0) {
  queue.shift(); // Remove one from the front
}

But something was strange. Inserting data with push finished in the blink of an eye, but removing data with shift took an incredibly long time.

Both are just one-line array methods, so why is the speed difference like night and day?

The reason is that every method has a different "Cost." Today, we will dig into the internal mechanics and the 'cost' of the JavaScript array methods we use without thinking.

1. Playing at the Back is Fast: push & pop

JavaScript arrays are stored sequentially in memory.

  • push(): Creates a spot at the very end of the array and inserts a value.
  • pop(): Deletes the value at the very end of the array.
  • These operations do not need to touch the existing data standing in line. You just place a chair at the end or take one away.

    Whether there is 1 item or 1 million items, the time it takes for this operation is roughly the same. This is called Constant Time (O(1)).

    2. Touching the Front Opens the Gates of Hell: shift, unshift & splice

    The problem arises when you touch the front or the middle of an array.

  • shift(): Deletes the element at index 0 (the very front).
  • unshift(): Adds an element to the very front.
  • splice(): Adds or deletes elements in the middle.
  • When Chris called queue.shift(), the following happened inside the JavaScript engine:

  • Delete the value at index 0. (This part is fast).
  • But the spot cannot be left empty. Indices must be filled sequentially starting from 0.
  • Pull the guy at index 1 to index 0.
  • Pull the guy at index 2 to index 1.
  • ...
  • Pull the guy at index 99,999 to index 99,998.
  • In other words, to delete one item from the front, you must move all remaining data one step forward (Re-indexing).

    If there are N items, N move operations are required. This is called Linear Time (O(N)). Since Chris called shift 100,000 times, internally, roughly 100,000 × 100,000 move operations occurred. It has to be slow.

    3. It's Not Magic: filter, map, slice, reduce

    "Then wouldn't it be faster if I used filter or map instead of a loop?"

    This is a common misconception among beginners.

    javascript
    // Keep only even numbers
    const evens = numbers.filter(num => num % 2 === 0);

    This code is very short, but internally, it is ultimately a loop that iterates from index 0 to the end of the array.

    If you put this code inside another loop? It immediately becomes the culprit of performance degradation. slice, reduce, and forEach are all the same. They are just Syntactic Sugar, not magic wands.

    4. JavaScript Array Method Cost Cheatsheet

    When solving algorithm problems or optimizing performance, keep this table in your head.

    MethodTaskCost (Time Complexity)Description
    pushAdd to endO(1) (Fast)Does not touch existing indices
    popDelete from endO(1) (Fast)Does not touch existing indices
    shiftDelete from frontO(N) (Slow)Moves all elements forward by one
    unshiftAdd to frontO(N) (Slow)Moves all elements backward by one
    spliceAdd/Delete middleO(N) (Slow)Moves elements after the changed position
    sortSortO(N log N)Slowest (High cost)
    filter/mapIterateO(N)Scans the whole array once
    arr[i]Get valueO(1) (Fast)Direct access via index

    5. Conclusion: What Should I Use?

    The reason Chris's Queue code was slow was that he ignored the characteristics of the data structure and overused the expensive shift.

    (When dealing with a lot of data, you should implement a Linked List yourself or use a method that avoids shift.)

  • Working only at the end of data? -> Array is the best. (push, pop)
  • Frequent work at the front of data? -> Array might be the worst choice.
  • "Short code does not mean fast code."

    Even when writing a single line of function, a developer must be able to imagine how many operations that function will perform internally.

    There is a way to express "how many operations" mathematically and elegantly. It is the lingua franca of developers, Big-O Notation.

    In the next post, let's find out exactly what symbols like O(1) and O(N) mean.

    Continuing in: "[Algorithm] Big-O Notation and Time Complexity: The Performance Report Card of Algorithms."

    🔗 References

  • MDN - Array.prototype.shift()
  • Time Complexity of JS Array Methods
  • Comments (0)

    0/1000 characters
    Loading comments...