【难以理解的js】Array.prototype.toString
问题来源
为何Array.prototype.toString.call({join(){ return 42 }})返回42,不应该也是[object Object]吗???
探究
首先有两点我们比较容易理解:
-
数组的
toString()实际调用了join()[2, 3, 4].toString() == '2,3,4'[2, 3, 4].join() === '2,3,4'- 可以通过修改原型上的
join方法,再调toString验证
-
Array的原型上实现了自己的toString方法,所以不会到Object的原型上去找toString
那么问题肯定就出在Array的原型上toString方法的实现
借助两大法宝:
ecma的规范:
v8的实现:
// https://tc39.github.io/ecma262/#sec-array.prototype.tostring
transitioning javascript builtin ArrayPrototypeToString(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
// 1. Let array be ? ToObject(this value).
const array: JSReceiver = ToObject_Inline(context, receiver);
// 2. Let func be ? Get(array, "join").
const prop: JSAny = GetProperty(array, 'join');
try {
// 3. If IsCallable(func) is false, let func be the intrinsic function
// %ObjProto_toString%.
const func: Callable = Cast<Callable>(prop) otherwise NotCallable;
// 4. Return ? Call(func, array).
return Call(context, func, array);
} label NotCallable {
return ObjectToString(context, array);
}
}
解析
借助规范和代码,可以得到toString的实现逻辑:
- 把
toString()的参数转化为对象,例子中{join(){ return 42 }}就是一个对象,这一步得到的array就是{join(){ return 42 }} - 获取
join属性 - 把
join转为方法 join能转为方法,就调用这个方法并返回,join不能转为方法,就调用Object.prototype.toString返回
至此真相大白,关键就在于获取了join
联系到前面说的:数组的toString()实际调用了join(),也就理解toString为何这么设计了
把
toString()的参数转化为对象,也有转换逻辑,这里不做讨论
总结
-
很显然,这不是bug;规范设计也是合理的
-
判断类型应该用
Object.prototype.toString