This is actually quite simple once you get how methods work in javascript behind the scenes.
toUpperCase
is a method. This is a function that operates on a specific object... usually via the this
variable.
Javascript is a prototypal language... meaning that the functions attached to objects are just functions and can be copied. There is some work behind the scenes that makes sure this
is set to the right thing when you call a method, but this work only happens when you call it as a method... as in the obj.method()
form.
In other words: ''.toUpperCase()
makes sure that this
is set to the string ''
when you call it.
When you call it as toUpperCase()
this
is not set to anything in particular (different environments do different things with this
in this case)
What your code does could be rewritten as this:
var function_to_call;
if (true) {
function_to_call = ''.toLowerCase;
} else {
function_to_call = ''.toUpperCase;
}
function_to_call();
Because your function call: function_to_call()
is not in the object.method()
syntax, the thing that sets this
to the correct object is not done, and your function call executes with this
not set to what you want.
As other people have pointed out, you can use func.call(thing_to_make_this)
or func.apply()
to attach the correct thing to this explicitly.
I find it much more helpful to use .bind()
- which is extremely under-used in my opinion. function_name.bind(this_object)
gives you a new function that will always have this
attached to the correct thing:
// assuming function_to_call is set
function_that_works = function_to_call.bind(my_object)
function_that_works(); // equivalent to my_object.function_to_call()
and this means you can pass around the function you get back from bind()
as you would a normal function, and it will work on the object you want. This is especially useful in callbacks, as you can create an anonymous function that is bound to the object it was created in:
// this won't work because when this runs, 'this' doesn't mean what you think
setTimeout(function() { this.display_message('success'); }, 2000);
// this will work, because we have given setTimeout a pre-bound function.
setTimeout(function() { this.display_message('success'); }.bind(this), 2000);
TL;DR: You can't call a method as a function and expect it to work, because it doesn't know what this
should be. If you want to use that function, you have to use .call()
, .apply()
or .bind()
to make sure this
is set correctly by the time the function executes.
Hope that helps.