Home > JavaScript, Tips & Tricks > Javascript function context, “apply” method and lost “this” object

Javascript function context, “apply” method and lost “this” object

How many times you had problem or were asked about what happened to this object in your function? Or why this is not an object you expect it to be? Probably these times you have to remind yourself or your fellow programmer about function context.
First of all I have to ask you all a pretty lame question. If you write function in JS block right inside your HTML, what this keyword will refer to?

<script type="text/javascript">
		function testThis(){
		  //who is "this"
		}
</script>

And now to the real example.

Let’s say you build a simple Javascript object:

<script type="text/javascript">
		var mathObj = {
			pi: 3.14,
			getCircumference : function(data) {
				alert(this.pi * 2 * data.radius);
			}
		}

		mathObj.getCircumference(10);
</script>

You have properties, you have functions, you can use this keyword to access properties of the object from its functions. Everyone is happy.
Now we want to add function that will get some server data using AJAX and run one of the functions our object has.
(To all who wondered what is $ stands for – I use JQuery framework in all my JS samples)

<script type="text/javascript">
		var mathObj = {
			pi: 3.14,
			getCircumference : function(data) {
				alert(this.pi * 2 * data.radius);
			},
			getRemoteCircumference: function(url) {
				$.getJSON(url, this.getCircumference);
			}
		}

		mathObj.getRemoteCircumference("RadiusData.js");
</script>

If we have {“radius”:5} in RadiusData.js file you would expect the result of getCircumference would be 31.4 but you will get NaN. Why is that? Because this keyword in this case will refer to some kind of object with request data returned by JQuery. What can you do?
Solution number one is to use your object name:

...
	getCircumference : function(data) {
		alert(mathObj.pi * 2 * data.radius);
	}
...

Ugly. You don’t have to return on object name in every function.
Solution number two is to use anonymous function and explicitly call your function:

....
	getRemoteCircumference: function(url) {
		$.getJSON(url, function(data){
			mathObj.getCircumference(data);
		});
	}
...

Good enough solution, but again I don’t like reusing object name…
And now to my favorite solution.

<script type="text/javascript">
   function getContextFunc(context, func) {
       return function() {
           func.apply(context, arguments);
       };
   }

   var mathObj = {
       pi: 3.14,
       getCircumference: function(data) {
           alert(this.pi * 2 * data.radius);
       },
       getRemoteCircumference: function(url) {
          $.getJSON(url, getContextFunc(this, this.getCircumference));
       }
   }

   mathObj.getRemoteCircumference("RadiusData.js");
</script>

Here apply function will come in handy. getContextFunc returns anonymous function that will run our original method in given context.
See some documentation

VN:F [1.9.22_1171]
Rating: 4.9/5 (8 votes cast)
Javascript function context, "apply" method and lost "this" object, 4.9 out of 5 based on 8 ratings
Categories: JavaScript, Tips & Tricks
  1. Brandon
    July 14th, 2009 at 00:54 | #1

    I might be missing something but couldn’t you just use:

    $.getJSON(url, mathObj.getCircumference));

    VA:F [1.9.22_1171]
    Rating: 3.0/5 (2 votes cast)
  2. July 14th, 2009 at 07:52 | #2

    @Brandon
    Sure you can, but you will not be able to use this in getCircumference cause it will run in the context of AJAX request object.
    There is a difference between mathObj.getCircumference() (calling object method) and mathObj.getCircumference (passing function reference as callback) this is what probably get you confused

    VN:F [1.9.22_1171]
    Rating: 0.0/5 (0 votes cast)
  3. M@
    November 3rd, 2009 at 00:35 | #3

    This solution can be cleaned up even further by putting getContextFunc into function thusly:
    Function.prototype.inContext = function(context){
    var func = this;
    return function(){
    return func.apply(context, arguments);
    };
    };

    So that when you want to call it you can simply say:
    getRemoteCircumference: function(url) {
    $.getJSON(url, this.getCircumference.inContext(this));
    }

    VA:F [1.9.22_1171]
    Rating: 5.0/5 (1 vote cast)
  1. July 12th, 2009 at 13:57 | #1
  2. July 12th, 2009 at 21:36 | #2
  3. July 15th, 2009 at 15:04 | #3
  4. April 3rd, 2010 at 00:46 | #4

Subscribe without commenting

SEO Powered by Platinum SEO from Techblissonline
watch free movies