原来JS还可以这样拆箱转换详解
前言
在读Winter大佬的《重学前端》栏目时,重温了JS的「拆箱转换」。「装箱转换」与「拆箱转换」以前都是了解的,今天来看,自己所谓的了解也真是一知半解。在阅读Winter老师写的内容后,对「拆箱转换」这个知识点还是不甚清楚,因此我再去深入地了解一番,参考资料详见文末的「参考链接」。
被我们忽略的表象
首先,我们来看一下例子:
consta={ name:'a', toString(){ console.log(this); console.log('toString'); return{name:'toString'}; }, valueOf(){ console.log(this); console.log('valueOf'); return{name:'valueOf'}; } }; a*2; //{name:"a",toString:ƒ,valueOf:ƒ} //valueOf //{name:"a",toString:ƒ,valueOf:ƒ} //toString //UncaughtTypeError:Cannotconvertobjecttoprimitivevalue a+""; //{name:"a",toString:ƒ,valueOf:ƒ} //valueOf //{name:"a",toString:ƒ,valueOf:ƒ} //toString //UncaughtTypeError:Cannotconvertobjecttoprimitive alert(a); //{name:"a",toString:ƒ,valueOf:ƒ} //toString //{name:"a",toString:ƒ,valueOf:ƒ} //valueOf //UncaughtTypeError:Cannotconvertobjecttoprimitivevalue
可以看到,toString和valueOf的执行顺序并不固定,而是根据某个条件来决定的,那么是根据什么呢?那就是在拆箱转换时,调用了对象的ToPrimitive内部函数时,其会根据执行上下文,自动传入一个转换类型参数,暂时给它命名为hint。
ToPrimitive
在JavaScript标准中,规定了ToPrimitive函数,它是对象类型到基本类型转换的实现者(即,拆箱转换);但这是一个内部算法,是编程语言在内部执行时遵循的一套规则。
对象到String和Number的转换都遵循“先拆箱再转换”的规则。通过拆箱转换,把对象变成基本类型,再从基本类型转换为对应的String或者Number。
但是对于不同的操作,拆箱转换的内部实现也有所区别,正如上面的例子所示。
「拆箱转换」的调用规则及顺序如下:
- 检查对象中是否有用户显式定义的[Symbol.toPrimitive]方法,如果有,直接调用;
- 如果没有,则执行原内部函数ToPrimitive,然后判断传入的hint值,如果其值为string,顺序调用对象的toString和valueOf方法(其中toString方法一定会执行,如果其返回一个基本类型值,则返回、终止运算,否则继续调用valueOf方法);
- 如果判断传入的hint值不为string,则就可能为number或者default了,均会顺序调用对象的valueOf和toString方法(其中valueOf方法一定会执行,如果其返回一个基本类型值,则返回、终止运算,否则继续调用toString方法);
来看一下第一种情况:
constb={ [Symbol.toPrimitive](hint){ console.log(`hint:${hint}`); return{}; }, toString(){ console.log('toString'); return1; }, valueOf(){ console.log('valueOf'); return2; } }; alert(b);//hint:string b+'';//hint:default b+500;//hint:default +b;//hint:number b*1;//hint:number
第二、三种情况:
constc={ toString(){ console.log('toString'); return1; }, valueOf(){ console.log('valueOf'); return2; } }; alert(c);//打印出toString并alert出1 c+'';//先后打印出valueOf,"2" c+500;//先后打印出valueOf,502 +c;//先后打印出valueOf,2 c*1;//先后打印出valueOf,2
那么关于hint可取的三种值,都有什么含义?又什么情况对应什么值?
确定hint取值
string
当在希望是字符串操作,也即发生对象到字符串的转换时,传入内部函数ToPrimitive的参数值即为string:
//output alert(obj); //usingobjectasapropertykey anotherObj[obj]=123;
number
当在希望是数值操作,也即发生对象到数值的转换时,传入内部函数ToPrimitive的参数值即为number:
//explicitconversion letnum=Number(obj); //maths(exceptbinaryplus) letn=+obj;//unaryplus letdelta=date1-date2; //less/greatercomparison letgreater=user1>user2;
default
当在一些不确定需要将对象转换成什么基础类型的场景下,传入内部函数ToPrimitive的参数值即为default:
//binaryplus lettotal=car1+car2; //obj==string/number/symbol if(user==1){...};
结语
如果亲爱的读者们在本文中发现了什么错误,或者有什么不同的意见,还请留言,一起讨论,一起将隐藏的、晦涩的点提出来,然后解决掉。
参考链接
- 《重学前端》——极客时间APP
- Objecttoprimitiveconversion
- ToPrimitive
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。