cf.completefrontendCode editorOpen lab
THE JAVASCRIPT FIELD GUIDE

Built-in prototypes

Learn where JavaScript’s built-in methods live, how arrays and functions inherit them, how to borrow methods safely, and why polyfills must be careful.

What you’ll be able to do
  • 01
    Read real prototype chainsWalk built-in chains with Object.getPrototypeOf and spot where methods live.
  • 02
    Borrow safelyUse call with array and object methods without changing built-ins.
  • 03
    Patch only with careExplain why extending built-ins is risky and how guarded polyfills differ from app ponyfills.

Built-in prototypes are the shared shelves

JavaScript gives arrays, functions, dates, maps, and ordinary objects useful methods without copying those methods onto every value. The methods live on built-in prototype objects. When code asks for a missing property, the lookup climbs the prototype chain you met in The prototype chain.

The curriculum summary for this lesson is short and important: built-in prototypes explain where array and string methods actually live. That includes Object.prototype, Array.prototype, Function.prototype, method borrowing, the danger of changing built-ins, and careful polyfills.

Real-life analogyThe city’s public libraries

Imagine every array carrying a library card. It does not own a private copy of every book. When it needs map, it visits the shared Array.prototype shelf. Most values can also reach the central Object.prototype reference desk.

In real life: A public library shelf
In JavaScript: A built-in prototype such as Array.prototype
In real life: Every resident can borrow a book
In JavaScript: Every array can use map, join, and friends
In real life: The central reference desk
In JavaScript: Object.prototype, near the end of most chains

Where the analogy stops: Books are borrowed physically. Prototype methods are just looked up and called; no method is copied onto the object.

Object.prototype: the common ancestor

INTERACTIVE

For most ordinary objects, the chain eventually reaches Object.prototype, then null. That is why objects, arrays, functions, maps, and dates can use old helpers such as hasOwnProperty, isPrototypeOf, and Object.prototype.toString. Modern code often prefers static helpers like Object.hasOwn(obj, key), because they work even when an object shadows or lacks hasOwnProperty.

Walk a real prototype chain
Prototype chain walkerPop out in the code editor (opens in a new tab)JavaScript
const value = [];let current = Object(value);while (current !== null) {  current = Object.getPrototypeOf(current);}
[]3 links
  1. 1. Array.prototypeat, concat, constructor, copyWithin, entries, every, fill, filter
  2. 2. Object.prototype__defineGetter__, __defineSetter__, __lookupGetter__, __lookupSetter__, constructor, hasOwnProperty, isPrototypeOf, propertyIsEnumerable
  3. 3. nullend of the chain
Try it yourself

Starting with [], JavaScript walks these prototype links with Object.getPrototypeOf until it reaches null.

This reads the real prototypes in your browser after the page mounts. Methods are sorted and shortened so the map stays readable.
Primitive methods connect here too

The unpublished Methods on primitives lesson covers autoboxing: "abc".toUpperCase() works because JavaScript temporarily treats the primitive like a string wrapper whose methods live on String.prototype.

Array.prototype and Function.prototype

STEP THROUGH

An array instance usually owns its elements and length. It does not own map, filter, join, or toString. Those are on Array.prototype. A function object does not own call, apply, or bind; it inherits them from Function.prototype.

Trace built-in method lookup
Step 0 of 5Ready
Your turn: follow the blue line

Predict where each method comes from. Step through the lookup from the array itself to its prototypes.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
const ownMap = Object.hasOwn(list, "map");const mapped = list.map((n) => n * 10);const arrayText = list.toString();const objectText = Object.prototype.toString.call(list);
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.
Function methods also live on a prototypePop out in the code editor (opens in a new tab)JavaScript
function greet(name) {  return "Hi, " + name;}const called = greet.call(null, "Ada");const bound = greet.bind(null, "Lin");const later = bound();

That code works because greet.call and greet.bind are found on Function.prototype. The published call, apply & bind lesson uses them to choose this and to borrow methods.

Common built-in prototypes
PrototypeCommon membersWho inherits from it
Object.prototypetoString, hasOwnProperty, isPrototypeOfMost ordinary objects, arrays, functions, dates, maps
Array.prototypemap, filter, join, at, toStringArrays such as [1, 2]
Function.prototypecall, apply, bind, toStringFunctions, classes, arrow functions
String.prototypetoUpperCase, slice, includesTemporary wrapper objects for string primitives
Map.prototypeget, set, has, entriesMaps created with new Map()

Borrowing methods

STEP THROUGH

Borrowing a method means: take a function from one prototype, call it once with a different receiver, and leave the prototype alone. It is like borrowing a library book for your own project, not scribbling in the book. This works only when the method’s expectations match the receiver. join is flexible because it needs numeric properties and length. map can also work on array-likes, but Array.from is often clearer when you want a real array.

Borrow join for an array-like
Step 0 of 4Ready
Your turn: follow the blue line

Change the separator, then step through how one borrowed method can work on an array-like object.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
const joined = Array.prototype.join.call(letters, "-");const copied = Array.from(letters);const label = Object.prototype.toString.call(copied);
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

Another practical borrowed method is Object.prototype.toString.call(value), the classic type-label trick discussed in Checking types reliably. It is not perfect—Symbol.toStringTag can customize it—but it explains why [1,2].toString() and Object.prototype.toString.call([1,2]) differ.

Why not to extend built-ins

SAFE SANDBOX

Changing a built-in prototype changes a shared public shelf. If app code adds Array.prototype.last, every array in that realm can see it. If it is enumerable, for…in over arrays sees it too. Even non-enumerable additions can collide with future standards or with other libraries.

Real-life analogyDo not scribble in public library books

Adding a method to a built-in prototype is not like putting a helper in your own module. It is like writing in the town’s shared book. Your note may be helpful to you, but everyone else now reads it too.

In real life: Writing notes in a public book
In JavaScript: Adding app methods to built-in prototypes
In real life: Everyone sees your notes
In JavaScript: Every value using that prototype sees the method
In real life: A future edition may use the same page
In JavaScript: A future standard method name may collide

Where the analogy stops: A real librarian can remove one marked book. A modified prototype affects all code in that JavaScript realm until changed back.

Run the danger demo in a sandboxed iframe
Sandbox-only codePop out in the code editor (opens in a new tab)JavaScript
Array.prototype.last = function () {  return this[this.length - 1];}; const keys = [];for (const key in ["red", "blue"]) {  keys.push(key);}console.log(keys.join(", ")); delete Array.prototype.last;
Sandbox outputposted message
Click Run. The real page prototypes are not changed.
Try it yourself

The iframe has its own built-ins. Adding an enumerable last method there makes for…in see it, then the snippet deletes it before reporting back.

The frame uses sandbox=allow-scripts, predefined code only, and the parent accepts results only from that iframe's window.
Web compatibility stories

The proposal that became Array.prototype.flat was renamed from flatten in 2018 for web compatibility after MooTools sites conflicted. Earlier, the array contains name was changed to includes after web compatibility problems, also involving MooTools-era code. The moral is stable: names on built-in prototypes are public territory.

Polyfills: missing pages, carefully taped in

CODE

A polyfill implements a standard feature in an older environment that lacks it. A good polyfill first feature-detects, then defines the method with descriptors that match the standard shape: writable and configurable, but not enumerable. It also tries to match edge cases, which is why production teams use tested packages such as core-js through tooling like Babel. The later Transpilers & polyfills lesson covers that workflow.

Real-life analogyA photocopied missing page

A polyfill is not a random patch. It is a careful replacement for a standard page that is missing from an old edition. If the page is already there, leave it alone.

In real life: Old book missing page 42
In JavaScript: Old browser missing Array.prototype.at
In real life: Tape in a copy only if page 42 is absent
In JavaScript: Feature-detect before defining
In real life: A separate handout
In JavaScript: A ponyfill helper such as at(array, index)

Where the analogy stops: Real pages do not have spec edge cases. Real polyfills must match details such as property descriptors, holes, coercion, and cross-browser bugs.

Guarded at polyfill shapePop out in the code editor (opens in a new tab)JavaScript
if (!Array.prototype.at) {  Object.defineProperty(Array.prototype, "at", {    value(index) {      const length = this.length;      const integer = Math.trunc(index) || 0;      const position = integer < 0 ? length + integer : integer;      return this[position];    },    writable: true,    configurable: true,    enumerable: false,  });}
A ponyfill leaves built-ins alonePop out in the code editor (opens in a new tab)JavaScript
function at(array, index) {  const length = array.length;  const integer = Math.trunc(index) || 0;  const position = integer < 0 ? length + integer : integer;  return array[position];}
Safe or risky?
  • Borrow Array.prototype.join with call for one object
  • Add debug to Object.prototype
  • A guarded, non-enumerable polyfill from a compatibility library
  • A separate at(array, index) helper
  • Monkey-patch console.log globally
  • Add last() to Array.prototype in an app
Try it yourself
0 of 6 correct

Sort each practice by whether it usually belongs in application code.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Where you’ll use this

You will use built-in prototype knowledge when debugging “is not a function”, checking whether a property is own or inherited, borrowing a method for an array-like value, reading older libraries, and deciding whether compatibility code belongs in your app or your build tooling.

  • Use Object.hasOwn(obj, key) to avoid inherited surprises.
  • Use Array.from(nodeList) when you want array methods on DOM collections.
  • Borrow with call when the method is generic and you want one precise call.
  • Prefer ponyfills in app code; leave broad polyfills to compatibility libraries.

Common misconceptions

“Methods are copied into every array.”

No. Arrays share methods through Array.prototype, which saves memory and keeps behavior consistent.

“Borrowing changes the borrowed-from prototype.”

No. call chooses this for one invocation. It does not relink prototypes.

“All prototype extension is a polyfill.”

A polyfill implements a standard missing feature. Adding your own convenience method is monkey-patching.

“If a method is non-enumerable, extending built-ins is safe.”

Non-enumerable avoids one loop problem, but collisions and semantic mismatches remain.

“Object.prototype is always present.”

Object.create(null) creates a null-prototype object. The next lesson, Object.create & null-prototype objects, explores that.

Practice: built-in prototypes

5 EXERCISES
Exercise 1 · Warm-upPredict the lookup

Predict both printed lines.

Starter codePop out in the code editor (opens in a new tab)JavaScript
const xs = [1, 2];
console.log(Object.hasOwn(xs, "map"));
console.log(xs.map((n) => n + 1).join("|"));

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeBorrow join for an array-like

    Work out what the borrowed method prints.

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    const buttons = { 0: "Save", 1: "Cancel", length: 2 };
    console.log(Array.prototype.join.call(buttons, " / "));

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeWrite a ponyfill last(array)

      Write a standalone function that returns the last item in an array.

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      function last(array) {
        return array[array.length - 1];
      }
      console.log(last(["red", "blue"]));

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · ChallengeRead a guarded polyfill

        What does the descriptor check print?

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        if (!Array.prototype.at) {
          Object.defineProperty(Array.prototype, "at", {
            value(index) {
              const length = this.length;
              const integer = Math.trunc(index) || 0;
              const position = integer < 0 ? length + integer : integer;
              return this[position];
            },
            writable: true,
            configurable: true,
            enumerable: false,
          });
        }
        const descriptor = Object.getOwnPropertyDescriptor(Array.prototype, "at");
        console.log(typeof descriptor.value);
        console.log(descriptor.enumerable);

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeExplain the rename

          The proposed array method name flatten collided with existing web code. What name did the standard choose instead?

          Answer, then press Check. Spacing and letter case don’t matter.

            Quiz: check your understanding

            7 QUESTIONS
            Lesson quiz · 7 questionsScore: first tries count
            1. Question 1 of 7Where does [1, 2].map usually find map?

              Choose an answer to see the explanation.

            2. Question 2 of 7What does this print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const xs = [1, 2];
              console.log(xs.toString());

              Choose an answer to see the explanation.

            3. Question 3 of 7What type label does this print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              console.log(Object.prototype.toString.call([1, 2]));

              Choose an answer to see the explanation.

            4. Question 4 of 7Why can Array.prototype.join.call({0: 'a', length: 1}, '-') work?

              Choose an answer to see the explanation.

            5. Question 5 of 7What is the biggest problem with adding app methods to built-in prototypes?

              Choose an answer to see the explanation.

            6. Question 6 of 7What should a careful polyfill do first?

              Choose an answer to see the explanation.

            7. Question 7 of 7Which statement about Function.prototype is true?

              Choose an answer to see the explanation.

            Key takeaways

            • Built-in methods usually live on shared prototype objects, not on each value.
            • Object.prototype is near the end of most chains, but null-prototype objects skip it.
            • Array.prototype provides array methods; Function.prototype provides call, apply, and bind.
            • Borrowing methods with call is local; extending built-ins is global and risky.
            • Polyfills fill missing standard features carefully; ponyfills avoid changing built-ins.

            Remember the one-liner.
            Built-in prototypes are JavaScript’s shared method shelves; use them, borrow from them carefully, and avoid writing your own notes on them.

            Up next: Object.create & null-prototype objects.

            CompleteFrontend Clear concepts. Working examples.