Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make an object subscriptable in Javascript?

In Python I can make an object subscriptable by implementing the __getitem__ method in a class:

class Room:
    def __init__(self, furniture):
        self.furniture = furniture

    def __getitem__(self, index):
        return self.furniture[index]

furniture = ['chair', 'TV', 'bed'] 
room = Room(furniture)
print(room[1]) # prints TV

As a result I can retrieve an item by only providing an index.

Can I achieve the same in Javascript? Currently I need to call the getitem method explicitly:

class Room {
  constructor(furniture) {
    this.furniture = furniture
  }

  getitem(index) {
    return this.furniture[index]
  }
}

let furniture = ['chair', 'TV', 'bed']
let room = new Room(furniture)
console.log(room.getitem(1))
like image 370
barciewicz Avatar asked Aug 10 '26 21:08

barciewicz


1 Answers

If you want to dynamically access numeric properties from the array, you can use a Proxy for your objects with the get trap to intercept calls to numeric properties and redirect them to the getitem method:

class Room {
  constructor(furniture) {
    this.furniture = furniture
  }

  getitem(index) {
    return this.furniture[index]
  }
}

const handler = {
  get: function(target, prop, receiver) {
    const numericProp = Number(prop);
    
    //verify the property is actually an integer
    if (Number.isInteger(numericProp)) {
      return target.getitem(numericProp);
    } else {
      //return what would be normally returned
      return Reflect.get(...arguments);
    }
  }
}

let furniture = ['chair', 'TV', 'bed']
let room = new Room(furniture)

const proxy = new Proxy(room, handler);
console.log(proxy[0]);// chair
console.log(proxy[1]);// TV
console.log(proxy[2]);// bed
console.log(proxy[3]);// undefined

room.furniture.push("table");

console.log(proxy[3]);// table

console.log(proxy.furniture); //["chair", "TV", "bed", "table"]

You can also use the construct trap to intercept the new keyword and return already proxied objects:

class Room {
  constructor(furniture) {
    this.furniture = furniture
  }

  getitem(index) {
    return this.furniture[index]
  }
}

const numericGetHandler = {
  get: function(target, prop, receiver) {
    const numericProp = Number(prop);
    
    //verify the property is actually an integer
    if (Number.isInteger(numericProp)) {
      return target.getitem(numericProp);
    } else {
      //return what would be normally returned
      return Reflect.get(...arguments);
    }
  }
}

const constructProxyHandler = {
  construct(target, args) {
    //call the constructor normally
    const instance = new target(...args);
    
    //return a proxy
    return new Proxy(instance, numericGetHandler);
  }
};

const ProxyRoom = new Proxy(Room, constructProxyHandler);

let furniture = ['chair', 'TV', 'bed']
let room = new ProxyRoom(furniture)

console.log(room instanceof Room); //true

console.log(room[0]);// chair
console.log(room[1]);// TV
console.log(room[2]);// bed
console.log(room[3]);// undefined

room.furniture.push("table");

console.log(room[3]);// table

console.log(room.furniture); //["chair", "TV", "bed", "table"]

Since the Proxy is essentially transparent, if you're using modules, you can just do export new Proxy(Room, constructProxyHandler) and thus when importing, you don't need to know nor care that Proxies are involved. You can just import Room from 'room.js' and treat Room as normal.

like image 75
VLAZ Avatar answered Aug 13 '26 11:08

VLAZ



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!