Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are methods of String.prototype available to string literals?

Tags:

javascript

This question has come out of another, which concerns the behaviour of console.dir with string literals. In particular, see the comments on my answer.

As we all know, String objects in JavaScript have a number of methods. Those methods are defined on the String.prototype object. String.prototype.toUpperCase for example. We can therefore do things like this:

var s = new String("hello"),
    s2 = s.toUpperCase();      //toUpperCase is a method on String.prototype

However, we can also do this:

var s = "hello",               //s is a string literal, not an instance of String
    s2 = s.toUpperCase();

Clearly, the JavaScript interpreter is doing some form of conversion/cast when you call a method of String.prototype on a string literal. However, I can't find any reference to this in the spec.

It makes sense, because otherwise you'd have to explicity cast every string literal to a String object before you could use any of the methods, and that would be quite annoying.

So my question is, where is this functionality described, and am I right in assuming the literal value is temporarily cast to an instance of String? Am I over-thinking this and missing something obvious?

like image 647
James Allardice Avatar asked Dec 20 '11 20:12

James Allardice


People also ask

Why are string literals used?

String literals are used to represent a sequence of characters which, taken together, form a null-terminated string. You must always prefix wide-string literals with the letter L.

Why do we have string literals in Java?

Why Java uses the concept of String literal? To make Java more memory efficient (because no new objects are created if it exists already in the string constant pool).

Why are string literals read only?

The string literal is stored in the read-only part of memory by most of the compilers. The C and C++ standards say that string literals have static storage duration, any attempt at modifying them gives undefined behavior. s is just a pointer and like any other pointer stores address of string literal.

What is the difference between string and string literal?

String literals or constants are enclosed in double quotes "" or with @"". A string contains characters that are similar to character literals: plain characters, escape sequences, and universal characters.


1 Answers

It's defined here:

The following [[Get]] internal method is used by GetValue when V is a property reference with a primitive base value. It is called using base as its this value and with property P as its argument. The following steps are taken:

  1. Let O be ToObject(base).
  2. Let desc be the result of calling the [[GetProperty]] internal method of O with property name P.
  3. If desc is undefined, return undefined.
  4. If IsDataDescriptor(desc) is true, return desc.[[Value]].
  5. Otherwise, IsAccessorDescriptor(desc) must be true so, let getter be desc.[[Get]].
  6. If getter is undefined, return undefined.
  7. Return the result calling the [[Call]] internal method of getter providing base as the this value and providing no arguments.

NOTE The object that may be created in step 1 is not accessible outside of the above method. An implementation might choose to avoid the actual creation of the object. The only situation where such an actual property access that uses this internal method can have visible effect is when it invokes an accessor function.

Source: http://es5.github.com/#x8.7.1

The primitive string value is coerced to an object in step 1.


Example 1

var str = 'some string';
str = str.toUpperCase();

Here, the expression str.toUpperCase is evaluated according to the semantics defined in 11.2.1 Property Accessors:

  1. The identifier str is evaluated according to identifier resolution (see 10.2.2.1 GetIdentifierReference). The result is a reference whose base value is the environment record of the current lexical environment, and whose referenced name is "str". This reference is the baseReference.
  2. The baseValue is determined by executing GetValue(baseReference). Since baseReference is not a property reference (its base value is not an object or a primitive value, but an environment record), the GetBindingValue() method is called in order to retrieve the value of the reference. This method returns the value of the local variable str, i.e. the primitive String value 'some string'. This value is the baseValue.
  3. The propertyNameValue evaluates to the primitive String value 'toUpperCase'. (I've shortened this process a bit for the sake of simplicity.)
  4. A new reference is created whose base value is baseValue and whose referenced name is propertyNameValue.

So, there are two references involved in this process:

  • str (base value: the environment record, referenced name: 'str')
  • str.toUpperCase (base value: 'some string', referenced name: 'toUpperCase')

Finally, the invocation operator () is executed on the latter reference. The value of that reference is determined according to the semantics defined at the top of this answer.

Example 2

var str = 'some string'.toUpperCase();

Here, the expression 'some string'.toUpperCase is evaluated according to the same "Property Accessor" semantics as in example 1:

  1. The string literal 'some string' obviously evaluates to the primitive String value 'some string'. This is the baseReference. (Don't let the name confuse you - this is a string value, not a reference.)
  2. The baseValue is determined by executing GetValue(baseReference). Since baseReference is not a reference, the method simply returns the argument value, i.e. baseValue = baseReference.

As you can see, just like in example 1, baseValue is the primitive String value 'some string'. The steps 3 and 4 are equivalent to the steps 3 and 4 in example 1.

So, both the identifier reference str and the string literal 'some string' evaluate to the same value - the primitive String value 'some string' - and that value is used as the baseValue for the new reference which is then invoked with (). And since this reference has a primitive base value, the semantics defined at the beginning of my answer apply.

like image 98
Šime Vidas Avatar answered Nov 03 '22 00:11

Šime Vidas