Proxies: Invoking static methods from the parent of the target object through a proxy

Here's an intriguing challenge for JavaScript enthusiasts

Can you intercept property access in JavaScript using proxies?

By applying a clever hack like the one demonstrated below, it's possible to intercept static property access within a class:

class Handler{
    constructor(object){
        this.object = object;
    }

    get(target, property){
        if (property === 'all') {
            return () => `selecting data from table: ${this.object.table}` ;
        }
        return target[property];
    }
}
class User{
    static table = 'users'
}

Object.setPrototypeOf(User, new Proxy({}, new Handler(User)));

console.log(User.all()); // prints "selecting data from table: users"

The issue arises when attempting to extend the User class and then invoking a static method from the parent class of User:

class Handler{
    constructor(object){
        this.object = object;
    }

    get(target, property){
        if (property === 'all') {
            return () => `selecting data from table: ${this.object.getTable()}` ;
        }
        return target[property];
    }
}

class Model{
    static getTable(){return this.table;}
}

class User extends Model{
    static table = 'users'
}

Object.setPrototypeOf(User, new Proxy({}, new Handler(User)));

console.log(User.all());

Executing this code will result in

TypeError: this.object.getTable is not a function
.

Upon closer inspection, I discovered that this.object is not a User class but rather some kind of a function.

Is there a way, through any workaround, to invoke the parent's static method getTable?

The second issue, even if you resolve the first one, is that instantiating the User class becomes impossible:

console.log(new User());
TypeError: Super constructor [object Object] of User is not a constructor

I suspect this is because User no longer functions as a class: console.log(User) yields ƒ [object Function] instead of class User extends Model

Does JavaScript support this type of functionality?

Answer №1

When working with your code, it becomes apparent that calling getTable() from the Model class within the User class is not possible anymore due to changes in their parent-child relationship.

To better understand this change, let's examine the prototype chain after running the provided code:

class Model{
    static getTable(){return this.table;}
}

class User extends Model{
    static table = 'users'
}

console.log(Object.getPrototypeOf(Model));
// Function (as class is a constructor function in JS)
// Model.[[Prototype]] === Function.prototype

console.log(Object.getPrototypeOf(User));
// Class Model with getTable() method
// User.[[Prototype]] === Model.prototype

Initially, the prototype chain follows the expected structure. However, upon executing

Object.setPrototypeOf(User, new Proxy({}, new Handler(User)));
, the inheritance is altered resulting in the following hierarchy:

const userPrototype = Object.getPrototypeOf(User);
console.log(userPrototype);
// Proxy {} (User now inherits from a Proxy instance)

const userPrototypePrototype = Object.getPrototypeOf(userPrototype);
console.log(userPrototypePrototype);
// Object instance methods (due to Proxy target being {})

Object.getPrototypeOf(userPrototypePrototype);
// null (end of prototype chain)

This modification removes Model from the chain, preventing direct access to its methods like getTable() from within User.

To achieve desired results without directly altering the prototype chain, an alternative solution is proposed:

class Handler{
    constructor(object){
        this.object = object;
    }
    
    get(target, property){
        if (property === 'all') {
            return () => `selecting data from table: ${this.object.getTable()}` ;
        }
        return target[property];
    }
}

class Model{
    static getTable(){return this.table;}
}

class User extends Model{
    static table = 'users'
}

class ProxyClass {
    constructor(object) {
        return new Proxy(object, new Handler(object));
    }
}

const UserProxy = new ProxyClass(User);

console.log(UserProxy.all());
// selecting data from table: users

console.log(new UserProxy());
// User {}

This approach allows dynamic wrapping of objects in a proxy while retaining access to inherited methods and class instantiation capabilities.

Important reminder! As noted on MDN page Inheritance with the prototype chain:

The someObject.[[Prototype]] notation refers to the prototype of someObject as per ECMAScript standard ac-cessed via Object.getPrototypeOf() and Object.setPrototypeOf(). This differs from the non-standard __proto__ property used by some browsers.

It should not be confused with func.prototype which assigns the [[Prototype]] to instances created by a function. The Object.prototype represents the base Object prototype.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

JavaScript checkboxes not being recognized by Node element

Located on the left side of the page, there is a feature that allows me to include selected options in a list with the same name attribute. These selections will then be sent as part of the entire form to the Node backend. Most of the elements on the page ...

Axios is causing my Pokemon state elements to render in a jumbled order

Forgive me if this sounds like a silly question - I am currently working on a small Pokedex application using React and TypeScript. I'm facing an issue where after the initial page load, some items appear out of order after a few refreshes. This make ...

Determine the type of a nested class within TypeScript

Utilizing nested classes in TypeScript is achieved through the following code snippet: class Parent { private secret = 'this is secret' static Child = class { public readSecret(parent: Parent) { return parent.secret } } } ...

Change not accepted

I am a beginner in Angular and still grappling with the fundamentals. On my menu, I have a cart icon with an initial value of 0 upon first load. In my product list, each product has an 'AddToCart' button. What I aim to achieve is- I want to dy ...

Is using debounce with $scope.$apply a logical choice?

In my experience, I have come across a method that claims to decrease the number of $digest loops by incorporating debouncing into the $scope.$apply process. It looks something like this: $scope.$apply = _.debounce($scope.$apply, 250); Is this approach v ...

Positioning broadcasted videos on a web page using the OpenTok API

Currently, I am utilizing opentok to connect to the broadcast service and obtaining the flash player object at the bottom of my page. I am seeking guidance on how to position it within a specific div container. The following code snippet demonstrates how ...

Enhance the sent server parameters by including extra options in fineuploader

I have successfully implemented file uploads using . Everything works perfectly. I am able to set parameters in the request object to send additional data to the server. However, when I try to add another parameter dynamically using the setParams function ...

"Switching from vertical to horizontal time line in @devexpress/dx-react-scheduler-material-ui: A step-by-step guide

Is there a way to switch the Time to a horizontal line using @devexpress/dx-react-scheduler-material-ui? <WeekView startDayHour={7} endDayHour={20} timeTableCellComponent={TimeTableCell} dayScaleCellComponent={DayScaleCell} /> Click ...

The challenge in displaying data from the backend using ajax in Vue.js 2.0 is hindering its visibility on the view

Currently, I am utilizing vue.js version 2.0, and the demo provided below is fully functional. <div class="container" id="app"> <ol> <li v-for="(todo,index) in todos"> {{ index }} {{ todo.text }} </li&g ...

Incorporate 'Additional features' into the Navbar when adjusting window size

When the window is resized, I want to display a collapsed 'More options' button that will collapse all hidden <li> elements. Here is an example: <li id="menu_more_container" class="dropdown" style="display: none; ...

Differences between count() and length() methods in Protractor

When it comes to determining the number of elements inside the ElementArrayFinder (which is the result of calling element.all()), you have two options according to the documentation: $$(".myclass").length, detailed here: This approach involves using ...

Issues with the directory for Chrome

Currently, I am utilizing jQuery and running an HTML file on my local machine without a server. Interestingly, the code works perfectly fine on Firefox but encounters issues on Chrome: $('#result').load('test.html'); It appears that t ...

Glitch found in Safari involving innerText of elements

Hey everyone, I posted this question not too long ago but now I have some images to share regarding the issue with Safari. When checking the console in Safari, the following text is displayed: <div id="rot3posDisp" class="rotDisp">C</div> Ho ...

Only the first iteration of a for loop is updating the array

This particular script is designed to work with an array of objects that are structured in the following way: {html:whatever number:number value}. function Org(data){ //array of objects var Data=data; for(var i=0; i<Data.length; i++){ var nums=[]; ...

The generated hook in vuejs is throwing an error stating that window/localstorage is not defined

My goal is to save an authenticated user to local storage and then load them into Vuex on the next page load. created () { let user = window.localStorage.getItem('user') if(user) { this.setUser(JSON.parse(user)) } } I initia ...

Sorting through names within a nested array based on specific criteria

I have been struggling to filter by item name for the past day and haven't been successful. The issue lies in my attempt to use a sample array for filtering. While I am able to filter by category successfully, the same cannot be said for filtering by ...

Troubleshooting minified JavaScript in live environment

Trying to wrap my head around AngularJS and Grunt. In my GruntFile.js, I have set up two grunt tasks for development and production. In production, I am using uglification to combine multiple js files into one. I am in need of some advice or tips on how t ...

Different ways of manipulating images with JavaScript

I attempted to tackle the issue independently, but I'm stumped. Is there a way to adjust the width of an image in JS when the screen dimensions are changed? ...

Is there a method to enable an anchor tag to be clickable when it's nested within a router-link?

Here's the code snippet for a component I'm working on: <template> <div id="news" class="news page"> <router-link class="news-card" :to="{ name: 'news-article'}"> < ...

What steps should I take to resolve a 'Missing environment variable' issue in the Sanity platform?

No matter what I've tried, I can't seem to fix the error that keeps occurring. An uncaught error is popping up, saying that the environment variable NEXT_PUBLIC_SANITY_DATASET is missing. http://localhost:3333/static/sanity-5377bc10.js:4605 ...