3

I have the following code:

var myPage = {};
myPage.component = function(callback){

    var somethingHappened = true;

    if (somethingHappened){
        callback();
    }
};

myPage.main = function(){

    // Initialise.
    this.init = function(){

        // make an instance of my component
        this.component = new myPage.component( this.callback );

        // need my utility function here
        this.doSomethingUseful();
    };

    // Callback to be executed when something happs in the component.
    this.callback = function(){
        this.doSomethingUseful(); // doesn't work
    };

    // A useful utility that needs to be accessible from both the 
    // init() and callback() functions
    this.doSomethingUseful = function(){
        // some utility stuff
    };
};
new myPage.main().init();

What's the best way for me to ensure that the myPage.main scope is available when the callback function is executed from the component?

Xoundboy
  • 657
  • 13
  • 24
  • The [`this` keyword](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/this) has nothing to do with the *variable* scope of the function – Bergi Oct 29 '12 at 18:30
  • better to correct someone rather than simply stating that they are incorrect. @Xoundboy you're thinking about context, not scope. – Ilia Choly Nov 01 '12 at 17:49

3 Answers3

6

use bind:

this.callback = function(){
    this.doSomethingUseful(); // doesn't work
}.bind(this);
Matt Whipple
  • 6,803
  • 1
  • 21
  • 34
  • This looks like a nice clean way of passing the scope but I'm not sure about the compatibility. Reading the page you linked it seems I need to include the fallback code for browsers that don't natively support the .bind() function. Still, for now I'll give it a shot and see if it breaks anywhere, and include it if necessary. – Xoundboy Oct 29 '12 at 22:49
  • The page includes a polyfill which is very simple. If you need to provide compatibility just copy and paste the code provided. – Matt Whipple Oct 30 '12 at 01:07
  • I just tried this out and it works nicely. I made a jsfiddle - http://jsfiddle.net/xoundboy/Vm5Tu/ – Xoundboy Oct 30 '12 at 11:43
2

If you want to supply scope, you can use Function.prototype.call.

var foo = 'bar';
function(){
  // this = 'bar';
}.call(foo);
Brad Christie
  • 96,086
  • 15
  • 143
  • 191
  • Thanks. I can't quite see how would that work in my code. Maybe you could spell it out - feeling a bit brain-fried tonight :) – Xoundboy Oct 29 '12 at 22:52
  • @Xoundboy: Having looked closer at what you're doing, you may want to use [`var self = this`](http://stackoverflow.com/questions/962033/what-underlies-this-javascript-idiom-var-self-this) so within those functions (`this.init`,`this.callback`,`this.doSomethingUseful`) you can refer to the scope of `myPage.main`. – Brad Christie Oct 29 '12 at 23:40
  • you're right - I can't believe it - this was the first thing I tried and it didn't work which is what prompted me to ask this question. I must have made a typo :/ - anyhow, I learned about .bind() and .call() as a result so not all bad. Here's the working example using your suggestion - http://jsfiddle.net/xoundboy/uxy7n/ – Xoundboy Oct 30 '12 at 12:01
0

Instantiate object with this function:

function newClass(klass) {
    var obj = new klass;

    $.map(obj, function(value, key) {
        if (typeof  value == "function") {
            obj[key] = value.bind(obj);
        }
    });

    return obj;
}

This will do automatic binding of all function, so you will get object in habitual OOP style, when methods inside objects has context of its object.

So you instantiate you objects not through the:

var obj = new myPage();

But:

var obj = newClass(myPage);