2

Here I have a just extracted my problematic part of code as it is an object with a clickInfo method that I want to use when testDivbtn in html is clicked​

var product = {
        jsondata:[
        {title:"GearBox", price:80000},
        {title:"Clutch", price:7000}
        ],
    loadInfo:function (event) {
    ​   // This line is printing first product info from the jsondata array​ - just to test
     console.log (this.jsondata[0].title + " " + this.jsondata[0].price);
      }
}

This is simple div that is attached to click handler

$ ("#testDivbtn").click(product.loadInfo); 

Here is html

<div class="col-sm6"><button id="#testDivbtn" type="button" class="btn btn-default">Test Product</button></div>

It is showing error in console - Cannot read property '0' of undefined I have this error at many other places in my app , If any one point the error it will be very helpful.

Vicky Gonsalves
  • 11,593
  • 2
  • 37
  • 58

2 Answers2

1

You need to use bind

The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

Ref : https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind

Initially when you use this.jsondata[0].title, here this refers to the <button> element, which doesn't have property .jsondata

$(function() {
  var product = {
    jsondata: [{
      title: "GearBox",
      price: 80000
    }, {
      title: "Clutch",
      price: 7000
    }],
    loadInfo: function(event) {
      console.log(this.jsondata[0].title + " " + this.jsondata[0].price);
    }
  }
  $("#testDivbtn").click(product.loadInfo.bind(product));
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="col-sm6">
  <button id="testDivbtn" type="button" class="btn btn-default">Test Product</button>
</div>

Polyfill:

if (!Function.prototype.bind) {
  Function.prototype.bind = function(oThis) {
    if (typeof this !== 'function') {
      // closest thing possible to the ECMAScript 5
      // internal IsCallable function
      throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }

    var aArgs   = Array.prototype.slice.call(arguments, 1),
        fToBind = this,
        fNOP    = function() {},
        fBound  = function() {
          return fToBind.apply(this instanceof fNOP
                 ? this
                 : oThis,
                 aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    if (this.prototype) {
      // Function.prototype don't have a prototype property
      fNOP.prototype = this.prototype; 
    }
    fBound.prototype = new fNOP();

    return fBound;
  };
}
Vicky Gonsalves
  • 11,593
  • 2
  • 37
  • 58
0

There you need to understand few concept here related to use "this" object properly. few methods like bind() -(from ES5 onwards), apply() and call(). Now as I see your code will try to explain this in little detail

Here the button $(“#testDivbtn”) is an object on its own, and you are passing the product.loadInfo method to its click() method as a callback, as it is inside product.loadInfo method will no longer refer to the product object. "this" will now refer to the object where the product.loadInfo method is executed because this is defined in that method. And the object that is invoking product.loadInfo is the button object, product.loadInfo will be executed inside the button object’s click method.

Even though you are calling the loadInfo () method with product.loadInfo() At this point, it should be apparent that when the context changes , when we execute a method on some other object than where the object was originally defined,

the "this" keyword no longer refers to the original object where "this" was originally defined, but it now refers to the object that invokes the method where this was defined.

Solution to fix this when a method is passed as a callback function: Since you really want this.jsondata to refer to the jsondata property on the product object, we can use the Bind (), Apply (), or Call () method to specifically set the value of this.

As above answer suggest to use bind replace all the place like this

    $("#testDivbtn").click (product.loadInfo);

with this

    $("#testDivbtn").click (product.loadInfo.bind (product)); // it will print Gearbox 80000 in console.

There is one question I am linking that here is really good to look for further detail about apply call bind Javascript call() & apply() vs bind()?

Community
  • 1
  • 1
Shad
  • 969
  • 1
  • 10
  • 18
  • What are the browsers you are using to run your app? – Shad Feb 14 '16 at 09:17
  • Oh I forgot to tell about my platform - I am running this app in IE 8 and my client need support for IE specially, as I know Jquery will deal cross browser by default. –  Feb 14 '16 at 09:20
  • bind is only available in EcmaScript5 and up. – Shad Feb 14 '16 at 09:24
  • 1
    there is an answer to refer for adding bind support in older browser http://stackoverflow.com/questions/11054511/how-to-handle-lack-of-javascript-object-bind-method-in-ie-8 – Shad Feb 14 '16 at 09:25
  • @Shad what is the difference in your answer? – Vicky Gonsalves Feb 14 '16 at 09:36
  • @Vicky I just explain same thing as in your answer. As OP need more explanation because OP wanted to understand the problem in more detail , that's y I added related questions as well. but any way your answer is also very helpful for this thread. – Shad Feb 14 '16 at 09:49