views:

234

answers:

3

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. 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 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)?

+1  A: 

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
Magnar
awesome! just what I wanted!
numeric
'self' is already a global variable, like 'undefined' and 'NaN'. Please use another name to make it less confusing.
some
+3  A: 

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
}};
Ferdinand Beyer
I was trying to simulate behavior of jquery object with o. As I noted in jquery, **this** is set to jquery object itself even when the function is being defined inside my object. I like Douglas Crockfords simple constructor pattern posted by Magnar.
numeric
A: 

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);
gustafc