-1

I am encountering a function is not defined .

I getting key value pairs and executing function on forEach.

the structure

var AppView = Backbone.View.extend({
  initialize: function() {
    this.render();
  },
  render: function() {
    this.$el.html("Hello World");
    this.one();
  },
  one: function() {
    this.two();
  },
  two: function() {
    var object = {
      "labname4": "423",
      "Path": "4",
      "X": "4"
    };
    console.log('two');

    Object.keys(object).map(function(objectKey, index) {
      var value = object[objectKey];
      this.three();
    });
  },
  three: function() {
    console.log('three');
  },
});

var appView = new AppView();
console.log("done");

Using:

  • jquery/1.7.2
  • underscore.js/1.3.3
  • backbone.js/0.9.2
Emile Bergeron
  • 17,074
  • 5
  • 83
  • 129
rusol7
  • 9
  • 2
  • move `this.three()` outside of the `Object.keys().map()` function – Kevin Kloet Nov 14 '17 at 09:02
  • Thank you Kevin, Ante Jablan Adamović was more precise since in need nested data. – rusol7 Nov 14 '17 at 09:12
  • Your code makes no sense and the obvious solution is to remove all the useless boilerplate. Without a clear [mcve], we can only tell you to use `bind` and even then, it's a dupe. – Emile Bergeron Nov 14 '17 at 16:20
  • Also, please upgrade to the latest Backbone, and use underscore since you're already loading it. `_.each(object, function(value, key){...}, this)` where you can pass the context directly. – Emile Bergeron Nov 14 '17 at 16:28
  • Possible dupe of [How does the “this” keyword work?](https://stackoverflow.com/q/3127429/1218980) – Emile Bergeron Nov 14 '17 at 16:33
  • [Iterate over object attributes and modify them](https://stackoverflow.com/q/26013802/1218980) – Emile Bergeron Nov 14 '17 at 16:35
  • [Why is my variable undefined inside the Underscore.js each function?](https://stackoverflow.com/q/13356203/1218980) – Emile Bergeron Nov 14 '17 at 16:35

2 Answers2

-1

Because you change the scope when you call Object.keys(object).map this will change too so you can't access the original value by using it. Aliasing it to that allows you still to access the original value of this. Your code will loook like this.

var AppView = Backbone.View.extend({

  initialize: function(){
    this.render();
  },
  render: function(){
    this.$el.html("Hello World");
    this.one();
  },

  one: function(){
  this.two();
  },
  two: function(){
  var that = this;
  var object = { "labname4": "423",
                "Path": "4",
                "X": "4"};
    console.log('two');

    Object.keys(object).map(function(objectKey, index) {
        var value = object[objectKey];
        that.three();
    });

  },
  three: function(){
    console.log('three');
  },
});
var appView = new AppView();
console.log("done");

Edit 1 var that = this vs bind

To me, it seems that .bind definitely has it's place. For instance when you have a function in a variable and you want to attach it to a particular this.

That said, in the case of nested anonymous functions I think that using var that = this; is straightforward and requires less mental accounting vs. using .bind. For example, say we have two nested functions:

function foo() {
  return (function() {
    doSomething((function() {
      return this.name; // what is `this` again?
    }).bind(this));
  }).bind(this);
}

vs

function foo() {
  var that = this;
  return (function() {
    doSomething((function() {
      return that.name; // `that` is easy to know because its defined 
    })
  })
}

Of course, because this is a matter of style the issue of "mental accounting" will differ from person to person. But to me, given the semtanics of Javascript's this, I think that that = this is easy to comprehend when you have nested callbacks.

V.S.V
  • 302
  • 2
  • 20
  • 1
    You are aware that `var that = this` is the old way of doing it and instead it's much cleaner to use `clojure` or `bind` ? – anteAdamovic Nov 14 '17 at 09:48
  • @AnteJablanAdamović please read edit 1 – V.S.V Nov 14 '17 at 10:24
  • @V.S.V What you're doing is very bad practice, nesting multiple callbacks just adds unnecessary level of complication and makes the code unreadable. Using `bind` helps slightly but isn't an optimal solution while `clojures` simply retain the scope without adding any extra code, furthermore they actually reduce the amount of code so they're far superior to anything else and make code cleaner and more readable. – anteAdamovic Nov 14 '17 at 10:57
-2

This is a common mistake when using nested functions like:

Object.keys(object).map(function(objectKey, index) {
            var value = object[objectKey];
            this.three();
        });

Here this.three(), this doesn't refer to your object anymore since it's within a nested functions scope and method three() isn't defined on it.

There are 2 possible solutions:

a) Instead of a nested function use a clojure. You can write the identical code like:

Object.keys(object).map((objectKey, index) => { // note the arrow and no 'function' keyword
            var value = object[objectKey];
            this.three();
        });

The clojure stays within the current scope so this.three() will work, function on the other hand defines it's own scope.

b) You can bind function if you want it to execute in a specific scope like:

Object.keys(object).map(function(objectKey, index) {
            var value = object[objectKey];
            this.three();
        }.bind(this)); // note '.bind(this)'

This way the function will use your current scope rather then making a new one. Both approaches are valid and it's up to the user to pick one, I personally prefer clojures.

anteAdamovic
  • 1,462
  • 12
  • 23
  • While you're right that `bind` is better than `var that = this`, `=>` is not a [closure](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures), it's an [arrow function expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions), and it's irrelevant in this question since it doesn't use ES6... – Emile Bergeron Nov 14 '17 at 16:25