Home > Software engineering >  JavaScript this is undefined when function called in a loop
JavaScript this is undefined when function called in a loop

Time:10-10

I have the following code with the behavior explained in the comments. What could be the reason why "this" is undefined in the anonymous function?

const getLength = (str) => {
  return str.length
}

const getDefaultValues = () => {
  const valueGetters = {
    A: {
      AA: {
        xpath: 'path',
        getValue: function () {
          return getLength(this.xpath) // <---- this is not undefined and returns value
        }
      },
      AB: {
        xpath: 'path',
        getValue: function () {
          return getLength(this.xpath) // <---- this is not undefined and returns value
        }
      },
      AC: {
        xpath: 'path',
        getValue: function () {
          return getLength(this.xpath) // <---- this is not undefined and returns value
        }
      },
      AD: {
        xpath: 'path',
        getValue: function () {
          return getLength(this.xpath) // <---- this is not undefined and returns value
        }
      },
      AE: {
        xpath: 'path',
        getValue: function () {
          return getLength(this.xpath) // <---- this is not undefined and returns value
        }
      },
      GROSS: function () { // <--- when called in the loop, this returns 0 because all the ternary operators evaluate to false and return 0
        // console.log('this', this) // <---- this is undefined
        const aa = this?.AA ? this.AA.getValue() : 0
        const ab = this?.AB ? this.AB.getValue() : 0
        const ac = this?.AC ? this.AC.getValue() : 0
        const ad = this?.AD ? this.AD.getValue() : 0
        const ae = this?.AE ? this.AE.getValue() : 0

        return aa   ab   ac   ad   ae
      }
    }
  }

  console.log('Calling without looping', valueGetters.A.GROSS()) // <--- This prints the correct value for gross without any problem

  // get the numerical values from the getters
  const valueEntries = Object.entries(valueGetters).map(([key, defaultValueGetter]) => {
    return [
      key,
      Object.entries(defaultValueGetter).map(
        ([defaultValueKey, defaultValue]) => [
          defaultValueKey,
          typeof defaultValue === 'function' ? defaultValue() : defaultValue.getValue() // <--- I suspect that the problem is with this line
        ]
      )
    ]
  })

  return makeObject(valueEntries)
}

const makeObject = (arr) => {
  return Object.fromEntries(
    arr.map(([key, val]) => (Array.isArray(val) ? [key, makeObject(val)] : [key, val]))
  )
}

console.log(getDefaultValues())

CodePudding user response:

You're just calling the function in the form defaultValue(), which doesn't provide a this context. You need to provide the this value explicitly, which you can do with .call().

typeof defaultValue === 'function' ? 
    defaultValue.call(defaultValueGetter) : 
    defaultValue.getValue()
  • Related