There's a difference between a primitive string value and a String
object.
'foo' // primitive
new String('foo') // object
When you call a String member function on a string primitive, it gets wrapped in a String object, which becomes the value of this
within the function. (More on this behavior below.)
So, in removeExtension
, this
is a String
object. On the other hand, the built-in function this.substr
returns a primitive; that's just how it's defined. Thus, you see different things when you return this
(String
object) versus the result of this.substr
(string primitive). If you want to return a string primitive version of the this
object, just use this.toString
.
You see the same wrapping behavior with any primitive, e.g., Number
s:
Number.prototype.returnThis = function() { return this; }
typeof 2; // 'number'
typeof (2).returnThis() // 'object' (a Number object, specifically)
If you really want to know why this happens, it's in the ECMAScript spec:
10.4.3 Entering Function Code
The following steps are performed when control enters the execution
context for function code contained in function object F, a caller
provided thisArg, and a caller provided argumentsList:
- If the function code is strict code, set the
ThisBinding
to thisArg
.
- Else if thisArg is null or undefined, set the
ThisBinding
to the global object.
- Else if
Type(thisArg)
is not Object, set the ThisBinding
to ToObject(thisArg)
.
- ....
Point #3 is the important on there: calling functions on primitive values (i.e., non-object) coerces them into their corresponding object form.