Search code examples
javascriptjqueryclosuresencapsulationjoose

Javascript scope referencing outer object


Basically, I use a meta-class framework called Joose for Javascript that allows me to make use of a more elegant class syntax - but I don't know how I might go about referencing the scope of the object from within deeper methods of the class declaration. I also use require.js for dependemcy management...

Here's an example class definition:

   define([
      'jquery',
      'handlebars',
   ], function($, Handlebars){

      var MyClass = Class("MyClass", {

         //inheritance         
         isa: SuperClass,

         //instance vars
         has: {

            hello:{
               is: 'r',
               init: 'Hi There!',
            },

            someVarToBeSetUsingAjax:{
               is: 'rw',
               init: false,
            },
         },

         //methods
         methods: {

            init: function () {

               var self = this;
               self.getAjaxVar();

            },

            getAjaxVar: function() {

               var self = this;

               //HOW CAN I REFERENCE 'self' THROUGHOUT MY OBJECT?

               $.get('ajax/test.html', function(response) {
                 self.someVarToBeSetUsingAjax = response.value;
               });
            },

            //lots more methods...

         }
    });

   return MyClass;

});

Ok, so my issue is - in the AJAX function I have to write var self = this to get my object into the scope of the AJAX call - no problem. But, I find myself doing this for almost every single method in my class declaration! How can I reference self in all of the methods in a clean and efficient way? I know you can use scope in AJAX by setting a parameter, assume it's not just AJAX but other functions that close the scope to the outside.

Thanks.


Solution

  • Everytime you nest a function, you have to think about this. But if you dont nest a function, or that function doesn't use this you don't need to think about it.

            init: function () {
               var self = this;
               self.getAjaxVar();
            },
    

    So in this case it's not necessary. This is exactly the same:

            init: function () {
               this.getAjaxVar();
            },
    

    But here:

            getAjaxVar: function() {
               var self = this;
    
               $.get('ajax/test.html', function(response) {
                 self.someVarToBeSetUsingAjax = response.value;
               });
            },
    

    You create an inner function, and you want a reference to the original value of this, so you do have to alias this to self to make it accessible.

    There isn't a way to fix this to a value from everywhere in your class.


    That said, you do have some options.

    Function.prototype.bind() can help.

    var func = function() { return this.name };
    var obj = { name: 'Bob' };
    var boundFunc = func.bind(obj);
    boundFunc(); // 'Bob'
    

    bind will return a new function with this always set to a specific object.

    So:

            getAjaxVar: function() {
               $.get('ajax/test.html', function(response) {
                 this.someVarToBeSetUsingAjax = response.value;
               }.bind(this));
            },
    

    Note this isn't supported in all browsers, you may need a shim for the old ones.

    Or just get used to self = this.


    I want to give a minor nod to coffeescript as well, because it supports declaration of functions that dont change the context when run.

    obj = {
      name: "bob"
      sayHello: ->
        doSomeAjax({
          success: =>
            alert "successfully made " + this.name + " say hello!"
        })
    }
    
    obj.sayHello()
    

    -> makes a normal function. But the fat arrow => will instead preserve the value of this inside and outside the function. It's very very handy in callbacks within instance methods. When compiled to JS, it basically does a self = this alias for you, using self within the inner function everytime to reference this. It's pretty slick.

    In plain JS though, the most common pattern is simply self = this, stick to it.