Question

I have the following:

var o = {f: function(fn) {
    fn.call(o);
}};
var ob = {f: function() {
    o.f(function() {
        this.x = 2; //HERE: how can this reference ob?
        //ob.x = 2;
    });
}};
ob.f();
ob.x; // undefined

o.f(fn) calls fn where this is bound to o.

At HERE, I want to use this to access ob. However, when ob.f is called, this is bound to o. JQuery works like this, I think. For example:

$(...).blah(function() {
    this // this is bound to $(...) jquery object.
    ...
};

What I'm doing now is:

var Ob = function() {
    var self = this;
    self.f = function() {
        o.f(function() { self.x = 2; };
    };
};
var ob = new Ob();
ob.f();
ob.x; // 2

But I don't like above for stylistic reasons:

  1. Using the new operator sounds like too classical OOP.
  2. Defining class Ob using function isn't intuitive (at least in the beginning).

That's why I am trying to define ob with an object literal. But I can't find a way to reference the object ob in a function that uses method call that sets this to other object than ob.

I can do something like the following:

var ob = {f: function() {
    o.f(function() {
        self.x = 2;
    });
}};
var self = ob;
ob.f();
ob.x;

But I don't know how to factor above. I tried:

function obj(o) {
    return function() {
        var self = o;
        return o;
    }();
}
var ob = obj({f: function() {
    o.f(function() {
        self.x = 2;
    });
}});
ob.f();
ob.x;// ReferenceError: self is not defined

So, is there a way to reference the object in a function inside the object reliably (this can bound to anything depending on the context)?

Was it helpful?

Solution

Following Douglas Crockfords simple constructor pattern, I would make a constructor-function that uses the object literal instead of new. Like this:

var o = {f: function(fn) {
    fn.call(o);
}};

function obj() {
    var me = {};
    me.f = function () {
        o.f(function() {
            me.x = 2;
        });
    };
    return me;
}

var ob = obj();
ob.f();
ob.x; // 2

OTHER TIPS

In JavaScript, functions are objects, having two methods to invoke the function:

call(scope, arg1, arg2, ...);
apply(scope, args);  // args is an array of arguments to call the function with

The first argument, 'scope', is the object that is bound to 'this' within the function. So, the following examples are equivalent:

obj.method(1, 2, 3);
obj.method.call(obj, 1, 2, 3);
obj.method.apply(obj, [1, 2, 3]);

In your first example, you are calling the function passed to o.f() using 'o' as scope:

var o = {f: function(fn) {
    fn.call(o);
}};

Therefore your function passed in 'ob' references 'o' as this:

var ob = {f: function() {
    o.f(function() {
        this.x = 2; //HERE: how can this reference ob?
        //ob.x = 2;
    });
}};

In the line 'HERE', 'this' is actually 'o'.

You could try the following:

var ob = {f: function() {
    var self = this;
    o.f(function() {
        self.x = 2; // self is ob now
    });
}};

Or you could modify the function 'o.f' to take a scope parameter:

var o = {f: function(fn, scope) {
    fn.call(scope || this); // Uses the given scope or this (= 'o') if no scope is provided
}};

Then you can pass 'this' in 'ob':

var ob = {f: function() {
    o.f(function() {
        this.x = 2; // 'this' will be the 'outer' this
    }, this); // Here: this as scope
}};

You can do it without auxiliary functions, just using literals:

var o = {f: function(fn) {
    fn.call(o);
}};
var ob = {f: function() {
    var self = this; // this == ob
    o.f(function() {
        self.x = 2; // self == enclosing function's this == ob
    });
}};
ob.f();
assert(ob.x == 2);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top