I have an object and it has another inner object. How can I call the parent object from the inner object?
var test = {
init: function () {
var instance = this;
},
call: function() {
this.stop(); // works
},
stop: function() {
this.parseText(); // works
},
parseText: {
load: function ()
{
this.call(); //*** dont work
instance.call(); // work, but what if i have this instance (same name) on another object, would'nt this conflict it?
}
}
};
I'm using an instance, which works fine, but what if I or someone wrote an instance (same name) var in another object, wouldn't it will conflict and overwrite this instance?
I have an object and it has another inner object. How can I call the parent object from the inner object?
var test = {
init: function () {
var instance = this;
},
call: function() {
this.stop(); // works
},
stop: function() {
this.parseText(); // works
},
parseText: {
load: function ()
{
this.call(); //*** dont work
instance.call(); // work, but what if i have this instance (same name) on another object, would'nt this conflict it?
}
}
};
I'm using an instance, which works fine, but what if I or someone wrote an instance (same name) var in another object, wouldn't it will conflict and overwrite this instance?
Share Improve this question edited Oct 30, 2011 at 10:41 Felix Kling 817k180 gold badges1.1k silver badges1.2k bronze badges asked Oct 30, 2011 at 10:15 BasitBasit 17.2k31 gold badges98 silver badges156 bronze badges 7-
2
Your examples doesn't work.
instance
is only visible in the scope ofinit
, and will not be usable insideparseText.load
– Eric Commented Oct 30, 2011 at 10:27 - like i told @Rob i wanted to use self/this or instance inside the object, rather then direct object name.. dont want to use object name inside functions, so how can i do that? – Basit Commented Oct 30, 2011 at 10:31
- @Basit: Look again at Eric's answer, he's giving you a reasonable way to do what you say you want (a working version of your updated example, which doesn't work). – T.J. Crowder Commented Oct 30, 2011 at 10:35
-
1
@Basit: That's exactly the same as our answers! The only difference is we named our variables
object
andobj
instead ofself
– Eric Commented Oct 30, 2011 at 10:36 - 1 This is a duplicate of stackoverflow./questions/6873281/… – T.J. Crowder Commented Oct 30, 2011 at 10:55
4 Answers
Reset to default 9Eric's answer gives you a reasonable example of how to do what you want to do, but doesn't really go into why.
In JavaScript, this
is set entirely by how a function is called (for now; see below the fold for details), not where the function is defined as it is in some other languages that have the same keyword (Java, C++, C#, ...).
You, the coder, determine what this
will be each time you call a function. There are two main ways: By calling the function via an object property (in the same expression), or explicitly using the function's built-in call
and apply
functions.
Via an object property
Using an object property:
obj.foo(); // or
obj["foo"](); // both work
That does two very distinct things, but which collaborate to set the this
value: First, the function reference is found by looking up the foo
property of the object obj
. Then, the function is called. Because you called it as part of the same overall expression retrieving the property value, the JavaScript engine will set this
to obj
within the call.
So in your example, test.parseText.load()
, within the load
call this
will be parseText
, not test
, because that's the object on which load
was looked up.
Note that setting-this
-via-property-lookup only works when they're done at the same time. This does not work:
var f = obj.foo;
f(); // `this` will not be `obj` within the call
That doesn't work because they weren't done at the same time. The property lookup and function call were separated.
Using call
or apply
The second way of setting this
is more explicit: All functions have the call
and apply
properties, which are themselves function references that call the function using information you supply. In both cases, the first argument is the object to use as this
during the call. So if we wanted to fix the example above that didn't work, we could do this:
var f = obj.foo;
f.call(obj); // `this` will be `obj` within the call
f.apply(obj); // same
The only difference between call
and apply
is how you supply function arguments. With call
, you supply them as further discrete arguments to the function; with apply
, you pass in an array of arguments.
So these all do the same thing:
// 1 - Directly via property
obj.foo("a", "b", "c");
// 2 - Using `call`
f = obj.foo;
f.call(obj, "a", "b", "c");
// 3 - Using `apply`
f = obj.foo;
f.apply(obj, ["a", "b", "c"]); // Note the `[ ... ]`, one array with three elements
You can see how call
and apply
could work with your existing structure:
test.parseText.load.call(test.parseText);
That calls test.parseText.load
, making this
= test.parseText
within the call.
What Eric did in his answer was to use a closure to make it simpler for you to call parseText
with the this
value you expect.
Further reading (disclosure: from my blog):
- Mythical methods
- You must remember
this
- Closures are not plicated
Up top I said:
In JavaScript,
this
is set entirely by how a function is called (for now...
The reason I said "for now" is that in ES6, JavaScript is getting "arrow functions" and unlike other functions, the value of this
within an arrow function is set by where they're created, not how they're called: They get this
from the context where you create them.
Suppose you were writing code in an object method and wanted to use another method of the object to, I don't know, output information from an array (yes, this is contrived). In ES5, you'd probably do this:
this.output("Entries:");
theArray.forEach(function(entry, index) {
this.output(index + ": " + entry);
}, this);
// ^------- tells `forEach` what to use as `this` during the callback
If you left off the argument, you'd have a bug:
this.output("Entries:");
theArray.forEach(function(entry, index) {
this.output(index + ": " + entry); // <== Bug, `this` is either
// `undefined` (strict) or
// the global object (loose)
});
But since arrow functions inherit this
from where they're created rather than getting it based on how they're called, the arrow function version of that doesn't need the second argument:
this.output("Entries:");
theArray.forEach((entry, index) => {
this.output(index + ": " + entry);
});
If all you're worried about is test
changing, do it like this:
var test = (function() {
var object = {}
object.call = function() {
this.stop(); // works
};
object.stop = function() {
this.parseText(); // apparently works, even though parseText is not a function
};
object.parseText = {
load: function() {
object.call(); // works
}
};
return object;
})();
If you don't know the name of test
, you can use a self-invoking anonymous function to create a wrapper, and refer to the object as shown below.
Note that test
is not a reference to a function, but to the return value of the anonymous function. Because the object name (obj
) is wrapped inside a function, it cannot be read or modified from outside
The solution below is neat, does not pollute the scope of test
, and works like a charm. As mentioned earlier, test
refers to the same object as obj
. It's however not possible to manipulate variable obj
, from outside, so that the code inside the function breaks.
var test = (function(){ //Self-executing function
var obj = {
call: function() {
this.stop(); // works
},
stop: function() {
this.parseText(); // works
},
parseText: {
load: function ()
{
obj.call(); // obj refers to the main object
}
}
};
return obj; //Return the object, which is assigned to `test`.
})(); //Invoke function
Update
It's not possible to reliably refer to self
, this
, or any reference to the object inside an object, without wrapping it.
Your current solution does not work, see ments in the code below:
var obj = {
init: function(){
var instance = this; //`instance` is declared using `var` inside a function
}, // This variable cannot read from "the outside"
parseText: {
load: function(){
instance.call(); //Does NOT work! instance is not defined
}
}
}
"call" is actually a built-in function on the function object that can be used to call the function specifying what to use for this. How does your code work? It doesn't seem like it should since parseText isn't a function...
Maybe try this:
parseText: function() {
var load = function ()
{
this.call(); //*** should work
};
load.call(this);
}