Reading Principles of writing consistent, idiomatic JavaScript in the section titled "Faces of this" it suggests that aliasing this in JavaScript is "extremely bug prone".
I personally try to use _.bind() (or something similar) whenever possible but does anyone know why aliasing this is so error prone?
There are four meanings this can take dependending on how it was invoked. Accordingly care must be taken to keep track of which this is being used, and I can think of this-prone problems in at least 3/4 of them.
Invoked as method
In obj.myFunc(), this binds to obj.
This one can be scary if myFunc is passed in a callback, as it will forget that it was once part of an object and be invoked standalone. See e.g. What does 'var that = this;' mean in JavaScript? for the usual workaround to this.
Invoked as standalone function
In plain myFunc(), this binds to global object.
Invoked as constructor
Invoked as new myFunc() (very different! All functions that are intended to be invoked with new should be capitalized, thereby looking like a pseudoclass). Creates a new object, binds it to this and (probably) returns that object.
Of course if you drop the new you will bind to the global object, which will likely clobber a lot of stuff and leave your program in a very broken state. The capitalization convention is very important, and lets this problem be picked up by JSLint (IIRC).
Invoked with apply (or call)
Invoked as myFunc.apply(obj, args), in which this binds to obj. Note this has security implications even, as any caller can swap out this with its own spoofed object.
this being aliases everywhere would be bug prone because it gets rather difficult (for the developer) to remember exactly what this is referring to in a given situation. This can easily lead to a developer using this believing it refers to one element, when in reality it is something totally different. For example:
$('#something').click ( function (e) {
//this refers to the clicked element
var _this = this; //Tracking a reference to the clicked element `this`
$.each(someArray, function(index, value) {
//this refers to the current element being iterated in someArray
$.ajax({
url : 'some/path',
success: function (res) {
//this refers to the ajax request
//_this still references the clicked element
}
})
})
})
Furthermore, if you need to access one this from within the scope of another this (for instance this clicked element from within the ajax call) you have to keep a reference to it somehow. I have marked this in the code.
Related
This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 1 year ago.
Today I ran into a very odd problem using Javascript's prototype and the this reference.
Basically, I have two objects. One object makes use of a div element and makes is possible to register an onclick event on the div. The other object makes use of this functionality and registers the onclick event using the first object.
Here is the code:
My first object, which takes care of the div:
DivObject = function() {};
DivObject.prototype.setClickListener = function(clickListener){
document.getElementById("myDiv").onclick = function(e){
clickListener(e);
};
};
My second object uses this functionality:
MainObject = function(){
this.myString = "Teststring";
this.divObj = new DivObject();
this.divObj.setClickListener(this.handleClick);
};
MainObject.prototype.handleClick = function(e){
// do something with e
};
The problem is, that inside MainObject.prototype.handleClick, the this reference refers to the window object, not to the MainObject. So, console.log(this) inside this function logs [Object window].
You can see this behaviour on jsfiddle.
As a workaround, I use the setClickListener function as follows:
var thisRef = this;
this.divObj.setClickListener(function(e){
thisRef.handleClick(e);
});
Now, the this reference in my handleClick function refers to my MainObject (as I want it to be).
See this workaround on jsfiddle.
My questions are:
Why does the this reference one time refer to the window object and one time to the this reference of my object? Is it overridden somewhere? I always thought using this in my object I can be sure that it is really the this reference of my object? Is the workaround I am using now the way how this problem should be solved or are there any other, better way to handle this case?
Your questions:
Why does the this reference one time refer to the window object and one time to the this reference of my object?
Other than using bind, the value of a function's this is set by how the function is called. When you do:
this.divObj.setClickListener(this.handleClick);
you are assigning a function reference, so the function is called later without any qualification (i.e. it's called as just handleClick rather than this.handleClick). On entering the function, because its this isn't set by the call, it will default to the global (window) object, or in strict mode remain undefined.
Is it overridden somewhere?
No, the value of this is set on entering an execution context. You can't overwrite it or directly assign to it, you can only set it in the call (e.g. as a method of an object, using new, apply, call) or using bind (also, arrow functions adopt the this of their enclosing lexical execution context).
I always thought using this in my object I can be sure that it is really the this reference of my object?
At the point you make the assignment, this is what you expect. But you are assigning a reference to a funciotn, not calling the function, so its this isn't set at that moment but later when it's called.
Is the workaround I am using now the way how this problem should be solved or are there any other, better way to handle this case?
Your work around is fine (and a common fix), it creates a closure so may have minor memory consequences but nothing serious. For very old versions of IE it would create a memory leak due to the circular reference involving a DOM object, but that's fixed.
The bind solution is probably better from a clarity and perhaps maintenance viewpoint. Remember to include a "monkey patch" for browsers that don't have built–in support for bind.
Please post code on SO, there is no guarantee that code posted elsewhere will continue to be accessible. The work around code:
MainObject = function(){
this.myString = "Teststring";
this.divObj = new DivObject();
var thisRef = this;
this.divObj.setClickListener(function(e){
thisRef.handleClick(e);
});
};
You could fix this by using .bind():
this.divObj.setClickListener(this.handleClick.bind(this));
See the demo.
This is probably pretty easy, yet I am confused, so maybe I might still learn something more today. I am trying to do the following:
var myObj = {};
myObj.src = {}
myObj.src.A = ['1.png','2.png','3.png'];
myObj.A = new Image();
myObj.A.src = this.src.A[0];
This will result in an Uncaught TypeError: Cannot read property 'A' of undefined error.
When I use myObj.src.A[0] instead of this it is working fine.
What would be the correct way to use this in this context?
this refers to the object context in which the code is executing. So if an object has a method aMethod, then inside aMethod this references the object that owns it.
I'm assuming your code there is running in the global namespace, so this is undefined. Really you just want myObj.A.src = myObj.src.a[0];.
http://www.quirksmode.org/js/this.html
The this keyword is always different depending on the scope involved. In the code snippet you've posted above, assuming that you're just putting this in the document somewhere in the head, this refers to the page itself. So, it's fairly obvious at that point that this.src.A will be undefined. If you were to apply an event to it with a delegate such as:
myObj.click = function() {
alert(this.src.A[0]);
}
The this keyword receives a new scope belonging to the owner of the delegate (in this case myObj). this is very easy to track so long as your clearly define your scopes and your scope boundaries.
this does not refer to var myObj in the case of your code. It is likely that if you are not inside the scope of a function or an objects method then this is referring to the DOM window which has no attribute src.A
this in JavaScript depends heavily on the context in which a function is called. If your code is (as it looks to be above) just hanging out in a script tag in the page, then this is going to be the "global" context -- which in the browser is the window object.
Generally, this refers to the object/scope a function belongs to, but there's a number of special (and useful) cases that happen because functions are first class values that can be assigned to different objects and invoked in various contexts.
Some lengthy elaboration others have written might be helpful:
https://stackoverflow.com/a/3320706/87170
http://yehudakatz.com/2011/08/11/understanding-javascript-function-invocation-and-this/
http://net.tutsplus.com/tutorials/javascript-ajax/fully-understanding-the-this-keyword/
https://developer.mozilla.org/en/JavaScript/Reference/Operators/Special_Operators/this_Operator
It can seem a little tricky at the start, particularly if you're used to languages in which this is always one thing, but after you learn a few rules it becomes fairly straightforward and actually very useful.
myObj.src.A[0] would be correct in this context because this references its immediate parent.
I have a script, that based upon specific scenarios, may need to supersede functions to do some processing before eventually calling the original function. (See "'overriding' Javascript Function")
I can get this working in general - here's a basic example using the jQuery plugin Tinyscrollbar (not my intended application, just something quick and easy to illustrate):
(function ($) {
// Snip..
function initalize() {
oSelf.update();
setEvents();
return oSelf;
}
// Snip..
function setEvents() {
(function () {
var oldInit = wheel;
wheel = function (oEvent) {
console.log('Intercept');
oldInit(oEvent);
}
})();
// Original event code, irrelevant to question
}
function wheel(oEvent) {
// Actual function, related to using the mousewheel
}
})(jQuery);
When I scroll the mousewheel, the console prints 'Intercept', and the scrollbar moves as originally defined. Wonderful!
However, the function name is hardcoded, and doesn't live in the global scope, so window[] is unavailable (which I like). Is there any possible combination of black magic, 'new Function()', and/or other way to loop through a potential list of function names (which may change based on other logic) and encapsulate them in this (or similar-in-spirit) manner?
Thanks in advance!
Unfortunately, there's no way to enumerate or dynamically access members in a scope object (with the convenient exception of the global scope/window object)
So you'd need to rephrase your code a bit. Instead of having free-floating functions in your outer function, have objects with methods on them. That'd make replacing those methods much easier.
There's some additional trickiness if you modify your functions after you started assigning them as event handlers or whatever. If you happen to use some kind of bind() wrapper around those functions, the correctness of your behavior will depend a lot on that bind() function.
Specifically, if you want the replacement method to retroactively become the method called for any event handler or callback it was assigned to, you'll want to use a bind() wrapper that takes a context object and a string meant to be the function name rather than a context object and a function reference. (and make sure that bind() doesn't resolve that string early to shave some ms on each calls.)
If don't don't want the retroactive behavior, you still have to make sure you don't have some bind()-ed version of the original method floating around and still being used for new callbacks after your replacement happened.
I am learning javascript and jquery and wondered whether it is good or bad practice to nest all my functions within $(document).ready(function). Is there any difference between this:
function someFunction()
{
return someThing;
}
$(document).ready(function()
{
// some code
...
someFunction();
});
and this:
$(document).ready(function()
{
// some code
...
function someFunction()
{
return someThing;
}
someFunction();
});
Be gentle - I'm pretty new to this!
You forgot at least one :-)
function someFunction()
{
return someThing;
}
$(someFunction);
Generally there is no difference between: $(document).ready(argFunc) and $(argFunc).
The other variations you listed have all different scopes for different things.
For example, in your 2nd block you declare someFunction inside a non-global scope, while your first and my example declare it in the global scope, which has implications for reachability.
Also, technically, with both of your variations you produce one extraneous function call. Because in this case, all you call is one function (which you can also write like my example).
UPDATE1:
To add some additional info and to further the discussion about scopes - JavaScript has very loose requirements for existence of variables. If it doesn't find them in the current scope, it wall just traverse the call chain upwards and look for it - until it finds it or not. That is why you can access the jQuery object ($) from anywhere.
There is however this:
someFunction($) {
// You can use $ here like always
return someThing;
}
$(someFunction);
This means, that the handlers (there can be more than one) for the document ready event of jQuery get passed jQuery itself as an argument.
If you specify this parameter for your function, you'll use that one, if you reference it. Otherwise, you are using the global one. That reduces the length of the upward look-up - chain.
That is completely irrelevant from a performance stand point.
But, by specifying this as a parameter, you make it absolutely clear where the jQuery object is coming from. Even that might be irrelevant.
I just wanted to show, that these callback-type functions in jQuery often take parameters that are useful.
So if you are ever stuck and need access to some object you don't have, it might be worthwhile to check the jQuery docs to see, if there is not a parameter, that does what you want.
To conclude this update, I very much like the first comment to the question, which is a much better answer than mine.
UPDATE2:
On the point of multiple callbacks for document ready (or any event binder in jQuery for that matter):
You can do this:
$(func1); // or $(document).ready(func1);
$(func2); // or $(document).ready(func2);
Both will get called as soon as jQuery fires its document ready event. That might come in handy depending on the perspective. Some would say, it encourages spreading your logic around. Others might say, it allows for cleaner separation of all the things that need to be done on document ready.
yes. The first way puts someFunction in the global scope so that it can be called by anyone. If you intend this function to be "private" and only callable by some code then the 2nd way is preferred. Generally you should prefer the 2nd way unless you need to export the function into global scope.
The differences are subtle, but worth bringing up.
Declared functions outside of DOM ready:
If you don't call the functions until DOM ready, then this is more efficient since it is registering the functions before waiting for the DOM to load (Edit: As is noted in the comments, there will be a slight performance penalty in accessing global variables due to scope resolution). This is very minor and probably not noticeable.
More importantly, you functions become globals, and you can clutter the global scope if you're not namespacing properly:
var myFuncs = {
someFunction : function() { ... },
someOtherFunciton : function() { ... }
};
Declared inside DOM ready:
Functions are no longer globals, but your functions are registered along with your other DOM ready code.
I would say it's fine to go either way in most cases, but definitely declare your functions under one namespace.
First off, functions are typically only declared if they are going to be used more than once. If you put a function inside the $(document).ready(function) then it won't be available outside the scope of the $(document).ready(function). Check out an article on Javascript Scope.
This is because $(document).ready accepts a function as a parameter and in your two examples you are declaring an inline function (that's the function () {} code in there). Check out a discussion of inline functions vs parameterized functions.
So, it boils down to deciding if you are going to use someFunction() more than once. If so, put it outside the $(document).ready function. Otherwise, you don't really need a function since you are just calling it once.
As long as someFunction() does not try to manipulate the dom, there is no difference. $(document).ready is called after all elements in the dom have loaded and can be accessed with javascript. If you try to manipulate an item in that page with javascript before it loads, it wont be there and the code will break.
Are there any benefits to using the 'window' prefix when calling javascript variables or methods in the window object? For example, would calling 'window.alert' have an advantage over simply calling 'alert'?
I can imagine using the prefix could give a small performance boost when the call is made from inside some function/object, however I rarely see this in people's code. Henceforth this question.
This is useful when attempting to test global object values. For example, if GlobalObject is not defined then this throws an error:
if(GlobalObject) { // <- error on this line if not defined
var obj = new GlobalObject();
}
but this does not throw an error:
if(window.GlobalObject) { // Yay! No error!
var obj = new GlobalObject();
}
Similarly with:
if(globalValue == 'something') // <- error on this line if not defined
if(window.globalValue == 'something') // Hurrah!
and:
if(globalObj instanceof SomeObject) // <- error on this line if not defined
if(window.globalObj instanceof SomeObject) // Yippee! window.prop FTW!
I would not expect to see a significant performance difference, and the only other reason you might do this is to ensure that you are actually getting a value from the global scope (in case the value has been redefined in the current scope).
I doubt there is any measurable performance benefit. After all the scope chain would be scanned for the identifier window first then the window object would be scanned for the desired item. Hence more likely it would be deterimental to performance.
Using window prefix is useful if you have another variable in scope that would hide the item you may want to retrieve from the window. The question is can you always know when this might be? The answer is no. So should you always prefix with window? What would you code look like if you did that. Ugly. Hence don't do it unless you know you need to.
Retrieved from Google (http://www.techotopia.com/index.php/JavaScript_Window_Object):
The window object is the top-level object of the object hierarchy. As such, whenever an object method or property is referenced in a script without the object name and dot prefix it is assumed by JavaScript to be a member of the window object. This means, for example, that when calling the window alert() method to display an alert dialog the window. prefix is not mandatory. Therefore the following method calls achieve the same thing:
window.alert()
alert()
However, I read but have not had time to test the following from:
(http://www.javascriptref.com/reference/object.cfm?key=20)
One place you'll need to be careful, though, is in event handlers. Because event handlers are bound to the Document, a Document property with the same name as a Window property (for example, open) will mask out the Window property. For this reason, you should always use the full "window." syntax when addressing Window properties in event handlers.
As far as performance, I think AnthonyWJones has it covered.
One use of the window prefix is to explicitly make something available outside the current scope. If you were writing code in a self-invoking function to avoid polluting the global scope, but there was something within that you did want to make globally available, you might do something like the following:
(function(){
function foo(){
//not globally available
}
function bar(){
//not globally available
}
window.baz = function(){
//is globally available
foo();
bar();
};
})();
I imagine that the performance benefit here is amazingly insignificant at best, if there is one at all.
It only matters if you're using frames and doing a bunch of javascript calls across frames, and even then only specific scenarios warrant the necessity of referencing window explicitly.
When you use the prefix, you're making it explicit you're using the "global" definition of the variable, not a local one. (I'm not sure whether / how you can inject variables into a scope in JS, except the weirdness with this and inline event handlers.) YMMV, you may either prefer the clarity, or find it to be just clutter.