liveBook · Manning

Negative indexing

As explained in Secrets of the JavaScript Ninja, Second Edition

An indexing convention that allows arrays to be accessed from the end using negative positions.

Negative indexing addresses array items from the end: -1 means the last item. JavaScript arrays do not support it natively, but a Proxy can add the behavior. section 8.2.4

A Proxy’s get and set traps convert the property key to a number. For a negative index, they add it to the array’s length; nonnegative indexes pass through unchanged. The same mapping lets you read and modify items. section 8.2.4

Reach the last item

Instead of ninjas[ninjas.length - 1], use proxiedNinjas[-1]. The proxy factory checks that its input is an array before creating the proxy. section 8.2.4

Listing 8.12. Negative array indexes with proxies
function createNegativeArrayProxy(array){
  if (!Array.isArray(array)) {                                #A
    throw new TypeError('Expected an array');                 #A
  }                                                           #A
  
  return new Proxy(array, {                                           #B
    get: (target, index) => {                                         #C
      index = +index;                                                 #D
      return target[index < 0 ? target.length + index : index];       #E
    },
    set: (target, index, val) => {                                    #F
     index = +index;
     return target[index < 0 ? target.length + index : index] = val;
    }
  });
}

const ninjas = ["Yoshi", "Kuma", "Hattori"];                          #G
const proxiedNinjas = createNegativeArrayProxy(ninjas);               #H

assert(ninjas[0] === "Yoshi" && ninjas[1] === "Kuma"                  #I
    && ninjas[2] === "Hattori",                                       #I
       "Array items accessed through positive indexes");              #I
                                                                      #I 
assert(proxiedNinjas[0] === "Yoshi" && proxiedNinjas[1] === "Kuma"    #I
    && proxiedNinjas [2] === "Hattori",                               #I
       "Array items accessed through positive indexes on a proxy");   #I

assert(typeof ninjas[-1] === "undefined"                              #J
    && typeof ninjas[-2] === "undefined" 
    && typeof ninjas[-3] === "undefined",
       "Items cannot be accessed through negative indexes on an array");

assert(proxiedNinjas[-1] === "Hattori"                                #K
    && proxiedNinjas[-2] === "Kuma"
    && proxiedNinjas[-3] === "Yoshi",
       "But they can be accessed through negative indexes");

proxiedNinjas[-1] = "Hachi";                                          #L
assert(proxiedNinjas[-1] === "Hachi" && ninjas[2] === "Hachi",        #L
       "Items can be changed through negative indexes");              #L

See the complete proxy factory, including its array check and read and write traps. section 8.2.4

How Secrets of the JavaScript Ninja, Second Edition covers it

How deep it goes
Implements get and set traps that convert keys and offset negative indexes by array length.
Examples
Compares ninjas[ninjas.length-1] with proxiedNinjas[-1] and demonstrates reading and modifying items.
What it assumes
Assumes familiarity with JavaScript arrays and Proxy traps; checks the input type but does not teach proxy fundamentals here.
What you can do after
Build it from scratch with a Proxy factory that maps negative indexes for array reads and writes.
How it connects
Connects JavaScript proxies with arrays and property access traps.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Secrets of the JavaScript Ninja, Second Edition. The section links point to the parts of the book it draws on.