I'm having a confusing problem using 'this' in javascript. I have a method 'get_data' which returns me some member variable of an object. Sometimes it returns to me the object itself... I have no idea why. Can someone explain what is happening here?
function Feed_Item(data) {
this.data = data;
this.get_data = function() {
return this.data;
}
this.foo = function() {
return this.foo2();
}
this.foo2 = function() {
//here type of this.data() == Feed_Item!!! It should be of type Data
}
this.bar = function() {
//here type of this.data() == Data, as I'd expect
}
}
What 'this' is in JavaScript depends on how you call the function. If 'this' is not bound to an object, this will be the window object.
If you call
item = new Feed_Item()
item.foo() //foo will be called with correct 'this'
But if you do Feed_Item(some_data), you will add a couple of functions to the global window object.
There are a lot of articles explaining this, e.g. http://www.digital-web.com/articles/scope_in_javascript/
A good blog post that explains "this" is available here: http://www.scottlogic.co.uk/2010/05/what-is-this/
Essentially the definition of this is:
The value of this is determined at the point at which the function is invoked, and is set to the object on which the function is invoked
However sometimes it's not easy to figure out exactly what that object is. This is because it depends on how the function is invoked. You can even dynamically set the value of this by invoking the function via its call method e.g.
window.str = "hello";
var fn = function(){
alert(this.str);
};
fn();
Running this code in the browser console gives hello which is the value of str on the global window object, however if you run:
fn.call({
str: 'goodbye'
}, []);
You get 'goodbye', as the context has been changed to the object passed in. Some libraries e.g. JQuery, ExtJS, ... make use of this feature to make then easier to use.
Related
I'm trying to set a field's value to a function, then execute it. this.fetchLocalStorage is not a function is what I get from running it.
var app = {
busdata: (function(){return this.fetchLocalStorage()})(),
fetchLocalStorage: function() {
//fetching
return "fetching data...";
}
};
console.log(app.busdata);
Note that by not making it a self-executing function, it works, but then it would mean the function is called everytime when I only need to fetch the data one time.
busdata: function(){return this.fetchLocalStorage()}
/* ... */
console.log(app.busdata()); //this calls the function every time :(
Thought it might a context problem so I tried a couple things with bind and call but with no luck.
Am I missing something?
this is only bound to the object when you call a method of the object, i.e. app.someMethod(). But you're trying to call fetchLocalStorage() when you're creating the object, not in a method of the object, so this is whatever the outer context is, which is likely the global window object.
You can't refer to other properties of the object until after the object has been created. So just call the function normally after you create the object.
var app = {
fetchLocalStorage: function() {
//fetching
return "fetching data...";
}
};
app.busdata = app.fetchLocalStorage();
I think your params were on the wrong side of the brace.
busdata: (function(){return this.fetchLocalStorage()}() ),
I'm new to JS and especially to prototypes.
I have this class and I cannot figure out how to access the properties.
var Lobby = function (preloader, serverConn) {
// Hold a reference to EventBus
this.serverConn = serverConn;
this.preloader = preloader;
this.scheduleItemService = new ScheduledItemService(this.preloader);
this.stage = new createjs.Stage("lobbyCanvas");
};
Lobby.prototype.start = function(me, signedRequest) {
sendMessage(data, function() {
// inside this scope this.stage is undefined!
renderLobbyImages(this.stage, this.scheduleItemService);
});
};
function renderLobbyImages(stage, scheduleItemService) {
stage.update();
};
Calling code:
var lobby = new Lobby(preloader, serverConn);
lobby.start(me, status.authResponse.signedRequest);
What am I doing wrong accessing 'renderLobbyImages' ??
Thank you :-)
In javascript, this is not resolved based on where it is declared/used. It is resolved when it gets called. (see: How does the "this" keyword in Javascript act within an object literal?).
Therefore, in the code above, since this is called in the callback to sendMessage(), and since sendMessage is asynchronous (meaning the callback will be called long after the call to start() have returned), this is therefore referring to the global object (which is window in web browsers, something unnamed in node.js).
So effectively, your code is doing this (no pun intended):
sendMessage(data, function() {
renderLobbyImages(stage, scheduleItemService);
});
Since there are no global variables called stage or scheduleItemService both are effectively undefined!
Fortunately, there is a workaround for this. You can capture the correct object in a closure:
var foo = this;
sendMessage(data, function() {
renderLobbyImages(foo.stage, foo.scheduleItemService);
});
Alternatively, you can pass the correct object (this) into an IIFE:
(function(x){
sendMessage(data, function() {
renderLobbyImages(x.stage, x.scheduleItemService);
});
})(this); // <-------- this is how we pass this
or:
sendMessage(data, (function(a){
return function(){
renderLobbyImages(a.stage, a.scheduleItemService);
}
})(this));
Or in this case, since stage and scheduleItemService are not functions, you can even pass them directly:
sendMessage(data, (function(a,b){
return function(){
renderLobbyImages(a,b);
}
})(this.stage, this.scheduleItemService));
There are lots of solutions to this problem. Just use the one you're most comfortable with.
Two problems.
this is missing in your constructor function on scheduleItemService.
Some functions you call to assign values seem to be not returning anything.
new createjs.Stage("lobbyCanvas");
new ScheduledItemService
Your calling method is alright.
this always refers to the calling object. When you say...
varlobby = new Lobby();
lobby.start();
... your calling object is lobby which has all the fields the start() function needs. But there initialization seems to be not working properly.
Please read this MDN starter guide.
Also we are having a some discussion about classical and prototype based OOP in this question. Please see the answer of Paul S for more about the tutorial I mentioned. Please see my answer if you need to see the tutorial in classical OOP light.
I have a function, functionWithDifferentScope, that takes an object, myobject.options, as a parameter. One of the pairs in the options object is a callback which points to a function defined in myObject: myCallback.
What I'm trying to achieve is injection of the myObject namespace into the callback of a function that is defined (by a 3rd party) at the global level.
A simplified example:
var myObject = {
options: {
callback: this.myCallback(this),
...,
},
init: function() {
// functionWithDifferentScope operates in the 'window' context
functionWithDifferentScope(this.options);
},
myCallback: function(namespace) {
// 'this' is window
// 'namespace' is myObject
}
}
myObject.init();
When executing this script, this.myCallback(this) appears to be executed at definition (due to the parenthesis?); as well as once myObject.init(); is caled. During the first executions this is myObject, but subsequent calls through the functionWithDifferentScope identify this as window.
Is there a way to pass the myObject namespace to the myObject.options.callback value as a parameter?
Do you mean this?
var myObject = new (function() {
var t = this;
vac callback = function() {
// t equals to the myObject-instance
// this equals to window
}
this.init = function() {
funcWithDifferencScope(callback);
}
})();
myObject.init();
I think what you are looking for is prototype style "bind"
Basically "this.myCallback(this)" is a call to the function.
this.myCallback is the function itself. (It is an object with the type function).
You can call it using the method 'call' or 'apply' that you can use on functions. Which will call these functions.
See:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FFunction%2Fapply
The first argument is the object context to work in. What I think you mean by object namespace.
so: a.callback(5) is the same as a.callback.call(a,5)
However please note that these days if you are working with most javascript libraries you probably have a 'bind' function that will do the work for you.
http://prototypejs.org/doc/latest/language/Function/prototype/bind/
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
the idea is that this.callback.bind(this) returns a Function object you can call that will inject the correct context automatically so you can pass the return value of bind alone as a callback and be assured that the method will be executed on the correct object.
I have defined the following module globally:
var module = (function () {
console.log(this);
this.fn = function () {
console.log(this);
}
return this;
})();
http://www.quirksmode.org/js/this.html :
In JavaScript |this| always refers to the “owner” of the function we're executing, or rather, to the object that a function is a method of.
The first call to console.log logs Window as the value of this, and that I understand. But, so does also the second call to console.log.
Since this refers to the owner of the function, why does module.fn log Window and not module?
When I call fn I still have to write module.fn, I can't write Window.fn. Since this refers to Window I find this confusing.
EDIT: I forgot to return this in my example.
Since this refers to the owner of the function, why does module.fn log Window and not module?
The return value of the outer function is window because it doesn't get called in any particular context, so module ends up being window as well.
It seems that the way you have applied the module pattern is wrong. It should be returning the public interface that gets used in the rest of your code:
var module = (function () {
console.log(this);
// public interface
return {
fn: function () {
console.log(this);
}
}
})();
module.fn(); // "Object {fn: function}"
In your example, the global object receives the fn. It is the window object in case of browsers. That is because you are calling the function in place (effectively constructing a new scope) without specific context.
On the end, your module object is just a reference to the window (because of return this;).
What is this?
In JavaScript, this is the current context, the object on which the function was called that particular time. It is not the "holder" of the function. You can always "steal" the method from other objects and apply (literally) it to your own.
Assume you want to slice the arguments object for some reason. It looks just like an array, but it is NOT an array. arguments.slice(2,4) does not work (assuming ECMAScript < 5). What to do?
Array.prototype.slice.apply(arguments, [2,4]);
You need to steal the slice function from the Array prototype, and use if on your arguments. Inside the slice call, the "this" is the arguments object that particular time.
How to construct a valid module?
Your job is to return the module object. You do not want do mess with the context. It is not relevant, as long as you are not applying the function directly on module object.
The easiest solution is the simplest.
var module = (function() {
// do something internally, great for "private" stuff
// then return the "public" interface
return {
doSomething: function() {
// do something
},
introduce: function() {
console.log(this);
}
};
})();
module.introduce(); // Object {doSomething: function, introduce: function}
module.doSomething();
The other way.
Alternatively, you could use the this to do your job, using the apply, if you really want to.
var module = {};
(function(){
this.doSomething = function() {
// do something
};
this.introduce = function() {
console.log(this);
};
}).apply(module);
module.introduce(); // Object {doSomething: function, introduce: function}
module.doSomething();
Note this is almost equal to the "new" call.
There are more equally valid ways to do it, but the first presented one is frequently used and very clear. Anyway, everything really depends on your code conventions.
Your pattern is wrong what you are doing to make a closed scope and setting module to the return from that scope:
// This is the design pattern:
var module = (function () {
var module = {};
var localVar = 1;
module.moduleVar = 2;
module.fn = function () {
console.log(this);
}
return module;
})();
console.log(module.fn); // function() { console.log(this) ;}
console.log(module.moduleVar); // 2
console.log(module.localVar); // undefined
Consider this:
window.onload = function () {
myObj.init();
};
var myObj = {
init: function () {
console.log("init: Let's call the callMe method...");
//callMe is not defined...
callMe();
//Works fine!
this.callMe();
},
callMe: function () {
console.log('callMe');
}
};
Since the init function gets called this way (myObj.init), I expect this to be myObj in the init function. And if that is the case, why the callMe function fails? How am I supposed to call the callMe function without using the this context in the init body? (Actually, it's too annoying to call the object methods using this over and over again through the functions. So what's the point of having a single object?)
I would like to know how can I fix this so that the callMe method gets called using the first invocation in the code above?
this is never implicit in JavaScript as it is in some other languages. Although there are ways to do it, like this using the with statement:
init: function () {
console.log("init: Let's call the callMe method...");
// Make `this` implicit (SEE BELOW, not recommended)
with (this) {
// Works
callMe();
}
},
...it's generally a bad idea. Douglas Crockford probably wrote one of the better descriptions of why it's a bad idea, which you can find here. Basically, using with makes it nearly impossible to tell what the code's going to do (and slows the code down, if you do anything else in that with statement that doesn't come from the this object).
This isn't the only way that JavaScript's this is not the same as it is in some other languages. In JavaScript, this is defined entirely by how a function is called, not where the function is defined. When you do this.callMe() (or the equivalent this["callMe"](), or of course foo.callMe(), etc.), two things happen: The function reference is retrieved from the property, and the function is called in a special way to set this to be the object that property came from. If you don't call a function through a property that way, the call doesn't set any particular this value and you get the default (which is the global object; window on browsers). It's the act of making the call that sets what this is. I've explored this in depth in a couple of articles on my blog, here and here.
This (no pun) can be made even clearer if you look at JavaScript's call and apply functions, which are available on all function objects. If I do this:
callMe.call({});
...it'll call the callMe function with a blank object ({}) as this.
So basically, just get used to typing this. :-) It's still useful to have properties and methods associated with an object, even without the syntactic convenience (and confusion!) of an implicit this.
You can also use the module pattern, which captures all private variables inside a closure, so you are free to use them without this, as they're in the same scope. You then pick and choose which methods/variables you want to make public:
var myObj = (function () {
var init = function () {
callMe(); // This now works
};
var callMe = function () {
...
};
// Now choose your public methods (they can even be renamed):
return {
init: init, // Same name
callMyName: callMe // Different name
};
}) ();
Now:
myObj.init(); // Works
myObj.callMyName(); // Works
myObj.callMe(); // Error