Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
149 views
in Technique[技术] by (71.8m points)

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

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...