Array.prototype.sort.call("foo"); // "[object Object]"
Array.prototype.sort.call(true); // true
Array.prototype.sort.call(1); // 1
Array.prototype.sort.call([1]); // [1]
Array.prototype.sort.call({}); // {}
Array.prototype.sort.call(function() {}); // function() {}
Why does the calling an array method on a string act differently? I presume it's because String
also has .length
and []
element accessors.
Can anyone explain what exactly happens when you call native methods on the wrong type?
[Edit]
Woops I solved it.
new Object("foo"); // "[object Object]"
It acts the same for the rest.
Let's speak to the ES5 spec:
15.2.1.1 Object ( [ value ] ) When the Object function is called with no arguments or with one argument value, the following steps are taken:
If value is null, undefined or not supplied, create and return a new Object object exactly as if the standard built-in Object constructor had been called with the same arguments (15.2.2.1).
Return ToObject(value)
And ToObject
is :
9.9 ToObject The abstract operation ToObject converts its argument to a value of type Object according to Table 14:
Table 14 — ToObject Argument Type Result
Undefined Throw a TypeError exception.
Null Throw a TypeError exception.
Boolean Create a new Boolean object whose [[PrimitiveValue]] internal property is set to the value of the argument. See 15.6 for a description of Boolean objects.
Number Create a new Number object whose [[PrimitiveValue]] internal property is set to the value of the argument. See 15.7 for a description of Number objects.
String Create a new String object whose [[PrimitiveValue]] internal property is set to the value of the argument. See 15.5 for a description of String objects. Object The result is the input argument (no conversion).
[Bounty]
Now why is [[PrimitiveVa开发者_高级运维lue]]
of String
equal to "[object Object]"
?
The 'problem' here is that the constructors for that type are called (Number, String, Array, Boolean...
), which act a little different than their "primitive" counterparts - they return an object that contains a primitive value.
This [[PrimitiveValue]], or [[value]] in previous specifications, is an internal property which you can access via .valueOf()
.
'foo' // is a string
String('foo') // is a string
new String('foo') // is a String object with [[value]] set to 'foo'
1 // is a number
Number(1) // is a number
new Number(1) // is a Number object with [[value]] set to 1
Object('foo') == (new String('foo'))
Object(1) == (new Number(1))
For some reason the webkit inspector doesn't seem aware of these object's primitive value. So, Object('foo')
will print '[object Object]', but if you call Object('foo').toString()
or any method that implicitly calls toString or valueOf, like alert(Object('foo'))
, you get the expected value "foo".
That's also the cause of this:
var x = new Boolean(false);
// any object evaluates to true...
!!x // == true
Boolean(x) // == true
Javascript has it's fair share of weirdness. Also consider that there are plenty of quirks in javascript engines, with varying levels of compliance with the specs.
In answer to your question
Now why is [[PrimitiveValue]] of String equal to "[object Object]" ?
You must turn to the ECMAscript standard doc section 15.4.4.11
"Let obj be the result of calling ToObject passing the this value as the argument."
When you Array.prototype.call("abc")
'this' is the string "abc". Passing that string into ToObject is the same as calling new Object("abc")
, and this is done implicitly.
Also, it is worthwhile noting that Array.prototype.sort.call("foo")
generates a TypeError in FF4.
TypeError: Array.prototype.sort.call("foo") is read-only
This is likey due the requirement (also in the spec) that the sort algorithm is not defined if "Any array index property of obj whose name is a nonnegative integer less than len is a data property whose [[Configurable]] attribute is false."
Strings can not be altered with [] accessors. Try it for yourself
var test = "abc";
test[1] = 'x';
console.log(test) //'abc'
FF4 is most likely being a bit more strict in following the spec
[[PrimitiveValue]]
of String
is not [object Object]
as demonstrated below:
console.log(String('foo'));
The issue in your code is the keyword new
console.log(new String('foo'));
The new
keyword will return an instance of Object
with the prototype of whatever type-constructor you passed.
I've found a workaround for this.
function callArrayMethod(method, arg1, arg2) {
if (arg2) {
return Array.prototype[method].call(arg1.split(''), arg2).join('');
}
return Array.prototype[method].call(arg1.split(''), arg2).join('');
}
Usage:
> callArrayMethod('sort', 'cba');
"abc"
精彩评论