How can I reference an object inside one of its member functions?
-
05-09-2019 - |
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:
- Using the
new
operator sounds like too classical OOP. - Defining
class Ob
usingfunction
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)?
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);