How and why does 'a'['toUpperCase']() in JavaScript work?

JavascriptFunction

Javascript 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 of String.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

  1. does not have a property named toUpperCase; or
  2. 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, but foo["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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMahesha999View Question on Stackoverflow
Solution 1 - JavascriptjAndyView Answer on Stackoverflow
Solution 2 - JavascriptThiefMasterView Answer on Stackoverflow
Solution 3 - JavascriptArtyom NeustroevView Answer on Stackoverflow
Solution 4 - JavascriptNiskView Answer on Stackoverflow
Solution 5 - JavascriptDenys SéguretView Answer on Stackoverflow
Solution 6 - JavascriptYaw BoakyeView Answer on Stackoverflow
Solution 7 - JavascriptShupingView Answer on Stackoverflow
Solution 8 - JavascriptStevePView Answer on Stackoverflow
Solution 9 - JavascriptLarsHView Answer on Stackoverflow
Solution 10 - JavascriptSanjayView Answer on Stackoverflow
Solution 11 - JavascriptAndrei BârsanView Answer on Stackoverflow
Solution 12 - JavascriptDaniel HaroView Answer on Stackoverflow