Home > OS >  How to bind a function to an object that is returned from a class method
How to bind a function to an object that is returned from a class method

Time:12-19

The title is probably worded horribly, but I'm not sure how to articulate what I'm trying to do here.

Essentially what I have is a class that contains two methods:

  1. An object factory class function objFactory that returns an object that contains three properties (value1,value2,value3) and another function bound to the object. I.e. myObj = {value1: 1, value2: 2, value2: 3, func()}
  2. Another class function changeValueProperties that will create values 1-3 based on an input value for myObj.

The user calls the factory method and an object is returned. The intent is that the user doesn't modify values 1-3 directly in the returned object, rather they call the built in method that computes those values and updates them on the object for the user. i.e. changeValueProperties is suppose to be a blackbox to the end user, they don't know how to calculate value1, value2, value3 for themselves.

The main issue here being the 'this' keyword. 'this' is referring to the MyTestClass, when I really want it bound to the returned object, so that its internal properties can be modified. I imagine something like this can be achieved via the call/apply/bind methods, but I'm not quite clear how to achieve that in this particular context. Especially when the updateValues method will use one of the class's own methods to do the computation.

I'm also open to alternative suggestions in terms of architecture. I realize this probably isn't kosher and there is probably a better method to implement the strategy I'm going for.

class MyTestClass {
    dummyData;
    constructor() {
        this.dummyData = 100;
    }
    changeValueProperties(value) {
        return {
            value1: value*1,
            value2: value*2,
            value3: value*3
        }
    }



    objFactory(value, ..other parameters) {
        
        let computedValues = this.changeValueProperties(value);

        let updateValues = (newValue) => {
            let computedValues = this.changeValueProperties(newValue);
            // This is where updateValues needs to update
            // values 1-3 in myReturnedObj that has been returned to the calling user.
            // this keyword can't be used, because it is bound to MyTestClass and not
            // myReturnObj
            this.value1 = computedValues.value1;
            this.value2 = computedValues.value2;
            this.value3 = computedValues.value3;

        }

        let myReturnObj = {
            value1: computedValues.value1,
            value2: computedValues.value2,
            value3: computedValues.value3,
            updateValues: updateValues
        }
        return myReturnObj
    }

}

const myClass = new MyTestClass();

let myObj = myClass.objFactory(10,...other parameters for object generation); 
// {value1: 10, value2: 20, value3: 30, ...}
myObj.updateValues(20); 
// should return {value1: 20, value2: 40, value3: 60, ...}

CodePudding user response:

There is a lot going on here, first of all, I think the arrow function does not bind this the way you intend. I recommend using a function instead.

function updateValues(newValue) { /* ... */ }

CodePudding user response:

So I ended up solving this after beautifulcoder pointed out that this does not behave the same way as a normal function. If you are stubborn and want to solve the problem using arrow functions however, you can use the following refactored code:

objFactory(value, ..other parameters) {
        
    let computedValues = this.changeValueProperties(value);

    let myReturnObj = {
        value1: computedValues.value1,
        value2: computedValues.value2,
        value3: computedValues.value3,
        updateValues: (newValue) => {
            let computedValues = this.changeValueProperties(newValue);
            myReturnObj.value1 = computedValues.value1;
            myReturnObj.value2 = computedValues.value2;
            myReturnObj.value3 = computedValues.value3;
        }
    }
    return myReturnObj
}

You can reference the object definition inside the object itself.

CodePudding user response:

It seems that you want this in updateValues to refer to two different objects. You have to decide on one of them, or neither.

To use neither, refer to the objects through variables:

objFactory(value) {
    const computedValues = this.changeValueProperties(value);
    const myTest = this;
    const myReturnObj = {
        ...computedValues,
        updateValues(newValue) {
            const newComputedValues = myTest.changeValueProperties(newValue);
//                                    ^^^^^^
            Object.assign(myReturnObj, newComputedValues);
//                        ^^^^^^^^^^^
    };
    return myReturnObj;
}

This uses the old var self = this pattern [1][2][3].

Now you can either

  • make updateValues a method that uses this to refer to the myReturnObj:

    objFactory(value) {
        const computedValues = this.changeValueProperties(value);
        const myTest = this;
        return {
            ...computedValues,
            updateValues(newValue) {
                const newComputedValues = myTest.changeValueProperties(newValue);
                Object.assign(this, newComputedValues);
    //                        ^^^^
        };
    }
    
  • or make updateValue an arrow function so that it uses this from its parent scope:

    objFactory(value) {
        const computedValues = this.changeValueProperties(value);
        const myReturnObj = {
            ...computedValues,
            updateValues: (newValue) => {
                const newComputedValues = this.changeValueProperties(newValue);
    //                                    ^^^^
                Object.assign(myReturnObj, newComputedValues);
        };
        return myReturnObj;
    }
    
  • Related