问题:类型转换的原理是什么?
解析:类型转换指的是将种类型转换为另种类型,例如:
varb=2;
vara=String(b);
console.log(typeofa);//string
当然,类型转换分为显式和隐式,但是不管是隐式转换还是显式转换,都会遵循定的原理,由于JavaScript是动态类型的语,可以随时赋予任意值,但是各种运算符或条件判断中是需要特定类型的,因此JavaScript引擎会在运算时为变量设定类型.
这看起来很美好,JavaScript引擎帮我们搞定了类型的问题,但是引擎毕竟不是ASI(超级智能),它的很多动作会跟我们预期相去甚远,我们可以从到试题开始.
{}+[]//0
答案是0
是什么原因造成了上述结果呢?那么我们得从ECMA-中提到的转换规则和抽象操作说起。
这是JavaScript种类型转换可以从原始类型转为引类型,同样可以将引类型转为原始类型,转为原始类型的抽象操作为ToPrimitive,后续更加细分的操作为:ToNumberToStringToBoolean。
为了更深的探究JavaScript引擎是如何处理代码中类型转换问题的,就需要看ECMA-详细的规范,从探究其内部原理,我们从这段内部原理示意代码开始.
//ECMA-,section9.1,page30.Usenull/undefinedfornohint,
//(1)fornumberhint,and(2)forstringhint.
functionToPrimitive(x,hint){
//Fastcasecheck.
if(IS_STRING(x))returnx;
//Normalbehavior.
if(!IS_SPEC_OBJECT(x))returnx;
if(IS_SYMBOL_WRAPPER(x))throwMakeTypeError(kSymbolToPrimitive);
if(hint==NO_HINT)hint=(IS_DATE(x))?STRING_HINT:NUMBER_HINT;
return(hint==NUMBER_HINT)?DefaultNumber(x):DefaultString(x);
}
//ECMA-,section8.6.2.6,page28.
functionDefaultNumber(x){
if(!IS_SYMBOL_WRAPPER(x)){
varvalueOf=x.valueOf;
if(IS_SPEC_FUNCTION(valueOf)){
varv=%_CallFunction(x,valueOf);
if(IsPrimitive(v))returnv;
}
vartoString=x.toString;
if(IS_SPEC_FUNCTION(toString)){
vars=%_CallFunction(x,toString);
if(IsPrimitive(s))returns;
}
}
throwMakeTypeError(kCannotConvertToPrimitive);
}
//ECMA-,section8.6.2.6,page28.
functionDefaultString(x){
if(!IS_SYMBOL_WRAPPER(x)){
vartoString=x.toString;
if(IS_SPEC_FUNCTION(toString)){
vars=%_CallFunction(x,toString);
if(IsPrimitive(s))returns;
}
varvalueOf=x.valueOf;
if(IS_SPEC_FUNCTION(valueOf)){
varv=%_CallFunction(x,valueOf);
if(IsPrimitive(v))returnv;
}
}
throwMakeTypeError(kCannotConvertToPrimitive);
}
上代码的逻辑是这样的:
1.如果变量为字符串,直接返回.
2.如果!IS_SPEC_OBJECT(x),直接返回.
3.如果IS_SYMBOL_WRAPPER(x),则抛出异常.
4.否则会根据传的hint来调DefaultNumber和DefaultString,如如果为Date对象,会调DefaultString.
5.DefaultNumber:先x.valueOf,如果为primitive,则返回valueOf后的值,否则继续调x.toString,如果为primitive,则返回toString后的值,否则抛出异常
6.DefaultString:和DefaultNumber正好相反,先调toString,如果不是primitive再调valueOf.
那讲了实现原理,这个ToPrimitive有什么呢?实际很多操作会调ToPrimitive,如加、相等或较操。在进加操作时会将左右操作数转换为primitive,然后进相加。
下来个实例,({})+1(将{}放在括号中是为了内核将其认为个代码块)会输出啥?可能常写代码并不会这样写,不过上出过类似的试题。
加操作只有左右运算符同时为String或Number时会执对应的%_StringAdd或%NumberAdd,下看下({})+1内部会经过哪些步骤:
{}和1先会调ToPrimitive{}会到DefaultNumber,先会调valueOf,返回的是,不是primitive类型,从继续到toString,返回[objectObject],是String类型最后加操作,结果为[objectObject]1再如有问你[]+1输出啥时,你可能知道应该怎么去计算了,先对[]调ToPrimitive,返回空字符串,最后结果为1。