1

Suppose I have the following coding scenario:

export const test = () => {
    return (
        var1,
        var2,
        var3
    ) => {
        return Object.freeze({
            getVarOne: () => var1,
            getVarTwo: () => var2,
            getVarThree: () => var3,
            total: () => var1 + var2 + var3,
            squareTotal: () => Math.pow(total(), 2)
        })
    }
}

let obj1 = test();
let obj2 = obj1(1, 2, 3);
let obj3 = obj2.squareTotal();

What is a way I can access the total method from the squareTotal method? I keep getting undefined for the total method call.

user1790300
  • 2,143
  • 10
  • 54
  • 123

4 Answers4

1

There is an undefined total function called as argument to Math.pow. If you intended to call the member of the object, then you need to specify that, as currently, it is a variable reference, not a property.

You can use this, but you must make the method a standard function instead of an arrow function -- you can use the ES6 object method notation (omitting the function keyword):

const test = () => {
    return (
        var1,
        var2,
        var3
    ) => {
        return Object.freeze({
            getVarOne: () => var1,
            getVarTwo: () => var2,
            getVarThree: () => var3,
            total: () => var1 + var2 + var3,
            squareTotal() { return Math.pow(this.total(), 2) }   
        })
    }
}

let obj1 = test();
let obj2 = obj1(1, 2, 3);
let obj3 = obj2.squareTotal();

console.log(obj3);  
trincot
  • 317,000
  • 35
  • 244
  • 286
  • What is squareTotal needed to use await/async using this same syntax or would I have to use the function keyword? How would that alter the syntax? – user1790300 Jul 18 '21 at 02:07
  • You can just prefix `async`, like `async squareTotal() { return Math.pow(this.total(), 2) }` – trincot Jul 18 '21 at 07:46
0

From inside of squareTotal() when called as you show in your example, you can do:

this.total()

this will be obj2 which is the frozen object that has the total method on it. But, you also need to stop using arrow functions for any methods that wish to reference your own object because arrow functions use the lexical value of this, not the called object value of this.

This will work with the two changes 1) change to this.total() and 2) change squareTotal() to regular function - not arrow function so it has the proper value of this:

const test = () => {
    return (
        var1,
        var2,
        var3
    ) => {
        return Object.freeze({
            getVarOne: () => var1,
            getVarTwo: () => var2,
            getVarThree: () => var3,
            total: () => var1 + var2 + var3,
            squareTotal: function() {
                return Math.pow(this.total(), 2);
            },
        })
    }
}

let fn = test();
let obj2 = fn(1, 2, 3);
let result = obj2.squareTotal();
console.log(result);
jfriend00
  • 683,504
  • 96
  • 985
  • 979
0

total is not a global function that you can call like total(). If you want to access the total method from the object you are returning, call this.total(). But make sure to not use arrow function expression.

You can use the below code:

const test = () => {
    return (
        var1,
        var2,
        var3
    ) => {
        return Object.freeze({
            getVarOne: () => var1,
            getVarTwo: () => var2,
            getVarThree: () => var3,
            total: () => var1 + var2 + var3,
            squareTotal: function() {return Math.pow(this.total(), 2) }
        })
    }
}    

let obj1 = test();
let obj2 = obj1(1, 2, 3);
let obj3 = obj2.squareTotal();

console.log(obj3);

Reason for changing from arrow function is that, arrow functions take this from the lexical scope of their declaration. Here it would have belonged to the window object. For any other method, this depends on how they are invoked(here they are invoked by the object). Relevant SO answer

Tushar Shahi
  • 16,452
  • 1
  • 18
  • 39
0

If you want to access properties within the same object, don't use arrow functions: they can't use the objects' scope. Here is a simplification of you factory function, where total is a getter and squareTotal a function expression for the instance.

const test = () => {
  return (var1, var2, var3) => {
    return Object.freeze({
      get total() {
        return var1 + var2 + var3;
        // ^ because the object is frozen
        //   var1 ... var3 don't have to be
        //   part of the object. Values are
        //   retrieved from closed over 
        //   parameter values
      },
      squareTotal() {
        return Math.pow(this.total, 2);
      },
    });
  };
}

const obj1 = test();
const obj2 = obj1(1, 2, 3);
console.log(`obj2.squareTotal(): ${obj2.squareTotal()}`);

// btw: because of the closure, the factory can 
// be simplified to a one liner too:
const test1a = () => (v1, v2, v3) => Object.freeze({
    squareTotal: () => Math.pow(v1 + v2 + v3, 2), });
const obj1a = test1a()(1, 2, 3);
console.log(`obj1a.squareTotal(): ${obj1a.squareTotal()}`);

// note: getter is not useful in a frozen object,
// but if the properties may change later it can be
// because in that case 'total' always gives the current
// total

const test2 = () => {
  return (var1, var2) => {
    return {
      var1,
      var2,
      get total() {
        return this.var1 + this.var2;
      },
      squareTotal() {
        return Math.pow(this.total, 2);
      },
    };
  };
};
const x = test2()(1, 2);
console.log(`x.sqareTotal(): ${x.squareTotal()}`);
x.var2 = 15;
console.log(`x.var2: ${x.var2}; x.squareTotal():  ${x.squareTotal()}`);
KooiInc
  • 119,216
  • 31
  • 141
  • 177