开发者社区> 问答> 正文

类型转换的规则有哪些?原理是什么?

展开
收起
前端问答 2019-11-24 20:24:49 1117 0
1 条回答
写回答
取消 提交回答
  • 前端问答小助手

    类型转换的规则有哪些?

    在if语句、逻辑语句、数学运算逻辑、==等情况下都可能出现隐⼠类型转换。

    image.png

    类型转换的原理是什么?

    类型转换指的是将⼀种类型转换为另⼀种类型,例如:

    var b = 2;
    var a = String(b);
    console.log(typeof a); //string
    
    

    当然,类型转换分为显式和隐式,但是不管是隐式转换还是显式转换,都会遵循⼀定的原理,由于JavaScript是⼀⻔动态类型的语⾔,可以随时赋予任意值,但是各种运算符或条件判断中是需要特定类型的,因此JavaScript引擎会在运算时为变量设定类型.

    这看起来很美好,JavaScript引擎帮我们搞定了 类型 的问题,但是引擎毕竟不是ASI(超级⼈⼯智能),它的很多动作会跟我们预期相去甚远,我们可以从⼀到⾯试题开始.

    {}+[] //0
    
    

    答案是0 是什么原因造成了上述结果呢?那么我们得从ECMA-262中提到的转换规则和抽象操作说起,有兴趣的童鞋可以仔细阅读下这浩如烟海的语⾔规范,如果没这个耐⼼还是往下看.

    这是JavaScript种类型转换可以从原始类型转为引⽤类型,同样可以将引⽤类型转为原始类型,转为原始类型的抽象操作为 ToPrimitive ,⽽后续更加细分的操作为: ToNumber ToString ToBoolean 。

    为了更深⼊的探究JavaScript引擎是如何处理代码中类型转换问题的,就需要看 ECMA-262详细的规范,从⽽探究其内部原理,我们从这段内部原理示意代码开始.

    // ECMA-262, section 9.1, page 30. Use null/undefined for no hint,
    // (1) for number hint, and (2) for string hint.
    function ToPrimitive(x, hint) {
    // Fast case check.
    if (IS_STRING(x)) return x;
    // Normal behavior.
    if (!IS_SPEC_OBJECT(x)) return x;
    if (IS_SYMBOL_WRAPPER(x)) throw MakeTypeError(kSymbolToPrimitive);
    if (hint == NO_HINT) hint = (IS_DATE(x)) ? STRING_HINT : NUMBER_HINT;
    return (hint == NUMBER_HINT) ? DefaultNumber(x) : DefaultString(x);
    }
    // ECMA-262, section 8.6.2.6, page 28.
    function DefaultNumber(x) {
    if (!IS_SYMBOL_WRAPPER(x)) {
    var valueOf = x.valueOf;
    if (IS_SPEC_FUNCTION(valueOf)) {
    var v = %_CallFunction(x, valueOf);
    if (IsPrimitive(v)) return v;
    }
    var toString = x.toString;
    if (IS_SPEC_FUNCTION(toString)) {
    var s = %_CallFunction(x, toString);
    if (IsPrimitive(s)) return s;
    }
    }
    throw MakeTypeError(kCannotConvertToPrimitive);
    }
    // ECMA-262, section 8.6.2.6, page 28.
    function DefaultString(x) {
    if (!IS_SYMBOL_WRAPPER(x)) {
    var toString = x.toString;
    if (IS_SPEC_FUNCTION(toString)) {
    var s = %_CallFunction(x, toString);
    if (IsPrimitive(s)) return s;
    }
    var valueOf = x.valueOf;
    if (IS_SPEC_FUNCTION(valueOf)) {
    var v = %_CallFunction(x, valueOf);
    if (IsPrimitive(v)) return v;
    }
    }
    throw MakeTypeError(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 ,返回的是 Object {} ,不是primitive类型,从⽽继续⾛到 toString ,返回 [object Object] ,是 String 类型 最后加操作,结果为 [object Object]1 再⽐如有⼈问你 [] + 1 输出啥时,你可能知道应该怎么去计算了,先对 [] 调⽤ ToPrimitive ,返回空字符串,最后结果为"1"。

    2019-12-19 07:36:28
    赞同 1 展开评论 打赏
问答分类:
问答地址:
问答排行榜
最热
最新

相关电子书

更多
低代码开发师(初级)实战教程 立即下载
冬季实战营第三期:MySQL数据库进阶实战 立即下载
阿里巴巴DevOps 最佳实践手册 立即下载