How and why does 'a'['toUpperCase']() in JavaScript work?
JavascriptFunctionJavascript Problem Overview
JavaScript keeps surprising me and this is another instance. I just came across some code which I did not understood at first. So I debugged it and came to this finding:
alert('a'['toUpperCase']()); //alerts 'A'
Now this must be obvious if toUpperCase()
is defined as a member of string type, but it did not make sense to me initially.
Anyway,
-
does this work because
toUpperCase
is a member of 'a'? Or there is something else going on behind the scenes? -
the code I was reading has a function as follows:
function callMethod(method) { return function (obj) { return obj[method](); //**how can I be sure method will always be a member of obj** } } var caps2 = map(['a', 'b', 'c'], callMethod('toUpperCase')); // ['A','B','C'] // ignoring details of map() function which essentially calls methods on every // element of the array and forms another array of result and returns it
It is kinda generic function to call ANY methods on ANY object. But does that mean the specified method will already be an implicit member of the specified object?
I am sure that I am missing some serious understanding of basic concept of JavaScript functions. Please help me to understand this.
Javascript Solutions
Solution 1 - Javascript
To break it down.
.toUpperCase()
is a method ofString.prototype
'a'
is a primitive value, but gets converted into its Object representation- We have two possible notations to access object properties/methods, dot and bracket notation
So
'a'['toUpperCase'];
is the access via bracket notation on the property toUpperCase
, from String.prototype
. Since this property references a method, we can invoke it by attaching ()
'a'['toUpperCase']();
Solution 2 - Javascript
foo.bar
and foo['bar']
are equal so the code you posted is the same as
alert('a'.toUpperCase())
When using foo[bar]
(note tha lack of quotes) you do not use the literal name bar
but whatever value the variable bar
contains. So using the foo[]
notation instead of foo.
allows you to use a dynamic property name.
Let's have a look at callMethod
:
First of all, it returns a function that takes obj
as its argument. When that function is executed it will call method
on that object. So the given method just needs to exist either on obj
itself or somewhere on its prototype chain.
In case of toUpperCase
that method comes from String.prototype.toUpperCase
- it would be rather stupid to have a separate copy of the method for every single string that exists.
Solution 3 - Javascript
You can either access the members of any object with .propertyName
notation or ["propertyName"]
notation. That is the feature of JavaScript language. To be sure that member is in the object, simply check, if it is defined:
function callMethod(method) {
return function (obj) {
if (typeof(obj[method]) == 'function') //in that case, check if it is a function
return obj[method](); //and then invoke it
}
}
Solution 4 - Javascript
Basically javascript treats everything as an Object, or rather every object can be viewed as a dictionary/associative-array. And functions/methods are defined the exact same way for the object - as an entry in this associative array.
So essentially, you're referencing/calling (notice the '()' ) the 'toUpperCase' property, of the 'a' object (which is a string type, in this case).
Here's some code of the top of my head:
function myObject(){
this.msg = "hey there! ;-)";
this.woop = function(){
alert(this.msg); //do whatever with member data
}
}
var obj = new myObject();
alert( obj.msg );
alert( obj['msg'] );
obj['woop']();
Solution 5 - Javascript
anyObject['anyPropertyName']
is the same as anyObject.anyPropertyName
when anyPropertyName
hasn't problematic characters.
See Working with Objects, from the MDN.
The toUpperCase
method is attached to the type String. When you call a function on a primitive value, here 'a'
, it is automatically promoted to an object, here a String :
> In contexts where a method is to be invoked on a primitive string or a > property lookup occurs, JavaScript will automatically wrap the string > primitive and call the method or perform the property lookup.
You can see the function exists by logging String.prototype.toUpperCase
.
Solution 6 - Javascript
So in Javascript, objects
are objects
. That is they're of this nature {}
. Object properties can be set using either of these: a.greeting = 'hello';
or a['greeting'] = 'hello';
. Both ways work.
Retrieval works the same. a.greeting
(without quotes) is 'hello'
, a['greeting']
is 'hello'
. Exception: if the property is a number, only the bracket method works. The dot method doesn't.
So 'a'
is an object with 'toUpperCase'
property which is actually a function. You can retrieve the function and call it subsequently either way: 'a'.toUpperCase()
or 'a'['toUpperCase']()
.
But imo the better way to write the map function would be as
var caps = ['a','b','c'].map( function(char) { return char.toUpperCase(); } )
Who needs the callMethod
function then?
Solution 7 - Javascript
Every JavaScript object is a hash table thus you can access its members by specifying a key. for example, if a variable is a string, then it should has the toUpperCase function. So, you could invoke it by
var str = "a"
str['toUpperCase'](). // you get the method by its name as a key and invoke it.
so, by inline str, you could have below
"a"["toUpperCase"]()
Solution 8 - Javascript
toUpperCase is a standard javascript method: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toUpperCase
The reason it works like 'a'['toUpperCase']()
is that the toUpperCase function is a property of the string object 'a'
. You can reference the properties on an object using object[property]
or object.property
. The syntax 'a''toUpperCase' indicates you are referencing the 'toUppercase' propert of the 'a' string object, and then calling it ().
Solution 9 - Javascript
> But does that mean the specified method will already be an implicit member of the specified object?
No. Someone could pass in an object that
- does not have a property named
toUpperCase
; or - has a property named
toUpperCase
that is not a function
In the first case, an error will be thrown because accessing a property that doesn't exist returns undefined
, and we can't invoke undefined
as a function.
In the second case, an error will be thrown because again, we can't invoke a non-function as a function.
Remember that JavaScript is a very loosely-typed language. Little or no type checking occurs unless and until it has to. The code you showed works in certain cases because, in those cases, the passed object has a property named toUpperCase
, that is a function.
The fact that the obj
argument isn't guaranteed to have the right types of properties doesn't bother JavaScript at all, so to speak. It adopts a "wait and see" attitude, and doesn't throw an error until an actual problem occurs at run time.
Solution 10 - Javascript
Almost everything in javascript can be treated as an object. In your case the alphabet itself acts as a string object and toUpperCase
can be invoked as its method. The square brackets are just alternative way of accessing object properties and since toUpperCase
is a method hence the simplebracket ()
is needed next to ['toUpperCase']
forming ['toUpperCase']()
.
'a'['toUpperCase']()
is equivalent to 'a'.toUpperCase()
'a'['toUpperCase']() // returns A
'a'.toUpperCase() // returns A
Solution 11 - Javascript
The important thing to note here is that, since Javascript is a dynamic language, every object is, essentially, just a glorified hash-map (with a few exceptions). And everything in a Javascript object can be accessed in two ways - bracket notation and dot notation.
I will quickly go over the two notations answering the first part of your question, and then I'll get to the second part.
Bracket notation
This mode is more similar to accessing hashmaps and arrays in other programming languages. You can access any component (data (including other objects) or function) using this syntax.
This is exactly what your are doing in your example. You have 'a'
, which is a string (and not a character literal, like it would be in a language such as C++).
Using the bracket notation, you access its toUpperCase
method. But accessing it is still not enough; simply typing alert
, for instance, in Javascript, doesn't call the method. It's just a simple statement. In order to call the function, you need to add the parenthesis: alert()
shows a simple dialog box containing undefined
, as it received no parameters. We can now use this knowledge to decipher your code, which becomes:
alert('a'.toUpperCase());
Which is much more readable.
Actually, a good way to understand this a little bit better is to execute the following Javascript:
alert(alert)
This calls alert
by passing it a function object, also alert
, without also executing the second alert. What is shown (in Chrome 26, at least) is the following:
function alert() { [native code] }
Calling:
alert(alert())
shows two consecutive message boxes containing undefined
. This is easy to explain: the inner alert()
gets executed first, shows undefined
(because it didn't have any parameters) and returns nothing. The outer alert receives the return value of inner alert - which is nothing, and also shows undefined
in a message box.
Try out all the cases on jsFiddle!
Dot notation
This is the more standard approach, which allows members of an object to be accessed using the dot (.
) operator. This is what your code would look like in dot notation:
alert('a'.toUpperCase())
Much more readable. So when should we use dot notation, and when should we use bracket notation?
Comparison
The main difference between the two methods is semantic. There are also some other details, but I'll get to those in a second. What's most important is what you actually want to do - a rule of thumb is that you use dot notation for well-established fields and methods an object has, and the bracket-notation for when you're actually using your object as a hash map.
A great example of why this rule is so important can be shown in your example - since the code is using bracket notation in a place where dot notation would have been much more sensible, it makes the code harder to read. And that's a bad thing, because code is read many more times than it is written.
In some cases, you have to use bracket notation even if using dot notation were more sensible:
-
if a member of an object has a name containing one or more spaces or any other special characters, you can't use dot notation:
foo.some method()
doesn't work, butfoo["some method"]()
does; -
if you need to dynamically access an object's members, you're also stuck using bracket notation;
Example:
for(var i = 0; i < 10; ++i) {
foo["method" + i]();
}
The bottom line is that you should use bracket syntax when using the object as a hash map (foods["burger"].eat()
) and the dot syntax when working with "actual" fields and methods (enemy.kill()
). With Javascript being a dynamic language, the line between "actual" fields and methods of an object and "other" data stored within can get pretty blurry. But as long as you don't mix them in confusing ways, you should be fine.
Now, onto the rest of your question (finally! :P).
> how can I be sure method will always be a member of obj
You can't. Try it. Try to call derp
on a string. You will get an error in the lines of:
Uncaught TypeError: Object a has no method 'derp'
> It is kinda generic function to call ANY methods on ANY object. But > does that mean the specified method will already be an implicit member > of the specified object?
Yes, in your case it would have to be. Otherwise you end up with the error I mentioned above. However, you don't have to use return obj[method]();
in the callMethod()
function. You can add your own functionality that then gets used by the map function. Here's a hard-coded method that turns all the letters into an uppercase letter:
function makeCap()
{
return function(obj) {
return obj.toUpperCase();
}
}
var caps2 = map(['a', 'b', 'c'], makeCap()); // ['A','B','C']
console.log(caps2)
The code in the tutorial you linked to uses partial functions. They're a tricky concept by themselves. Reading more about that subject should help make things clearer than I could ever make them.
Note: this is the code of the map function used by the code in the question, source here.
function map(arr, iterator) {
var narr = [];
for (var i = 0; i < arr.length; i++) narr.push(iterator(arr[i], i));
return narr;
}
Solution 12 - Javascript
If your asking how it actually work which is how I read it. Ok this is a simple mathematical function. To understand it you need to look at the ascii table. Which assigns a numerical value to each letter. To covert it the compete simply uses a logic statement to covert so for example If(ChcrValue > 80 && charValue < 106) //This is the set of lower case letters then charValue = charValue - 38; // the distance between the lower set and the upper set
it's that simple, I did not actually bother to look up the correct values however this is basically shifting all lower case letters to uppercase value.