4

Let's say I have the following code:

class Test {
  constructor(obj) {
    this.obj = obj
  }

  change() {
    Object.keys(this.obj).forEach(function(name, index) {
      alert(this.obj[name])
    })
  }

}

objct = {
  n1: 1,
  n2: 2
}

var test = new Test(objct)
test.change()

However, when I run this, I get the following error:

Uncaught TypeError: Cannot read property 'obj' of undefined

I believe it means that this is undefined inside the object keys function. How can I access this.obj inside the Object keys and forloop?

As per this answer, I can use map, but I need the property name and the array index inside the forloop as well. And here's a fiddle of the code, if it may help you. Thank you!!!

2
  • 1
    try changing this function: function (name, index) { alert(this.obj[name]) }) into an arrow function to get 'this' context Commented Mar 17, 2019 at 17:43
  • Just don't use forEach. Use for … in or for … of. Commented Mar 17, 2019 at 18:35

4 Answers 4

6

That's because the function-context (the this) of the function you're passing into forEach() is now window and it's no longer your Test instance.

If you'll use an arrow-function instead, you'll be able to preserve the lexical-scope and your this would point to the current (Test) instance:

Object.keys(this.obj).forEach((name, index) =>
{
    alert(this.obj[name])
});

See MDN

Sign up to request clarification or add additional context in comments.

1 Comment

Actually it's undefined, not window.
5

this refers to the current context of the function and hence in your example this.obj looks for obj in the context of forEach callback. You can solve this by keeping a reference to current context i.e. this OR use an arrow function instead.

OLD WAY: In this solution, we keep a reference to the current context in variable self

class Test {
  constructor(obj){
    this.obj = obj
  }

  change() {
    var self = this
    Object.keys(this.obj).forEach(function (name, index) {
    alert(self.obj[name])
  })
  }

}

objct = {
  n1: 1,
  n2: 2
}

var test = new Test(objct)
test.change()

Preferred or Shorthand Solution: By using Arrow Functions

class Test {
  constructor(obj){
    this.obj = obj
  }

  change() {
  Object.keys(this.obj).forEach((name, index) => {
    alert(this.obj[name])
  })
  }

}

objct = {
        n1: 1,
      n2:2
}

var test = new Test(objct)
test.change()

1 Comment

thank you for the answer, it was interesting to know about self, having not used it before. JS is changing fast :)
4

A classic scope lesson in Javascript.

There are two ways to do this:

Using bind() which binds the scope of your forEach to the parent function

change() {
  Object.keys(this.obj).forEach(function(name, index) {
    alert(this.obj[name])
  }.bind(this))
  }

using => which is another way of binding the scope of your forEach to the parent function

change() {
  Object.keys(this.obj).forEach((name, index) => {
    alert(this.obj[name])
  })
  }

1 Comment

thank you for the answer. bind looks like a workaround when working with older browser compatibility :) (yes, i dont like babel)
1

The anonymous functions are not bound to the context, so they doesn't have access to this; But the arrow functions are. So use that instead.

class Test {
  constructor(obj) {
    this.obj = obj
  }

  change() {
    Object.keys(this.obj).forEach((name, index) => {
      alert(this.obj[name])
    })
  }

}

objct = {
  n1: 1,
  n2: 2
}

var test = new Test(objct)
test.change()

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.