JavaScript关于类型转换

一个关于JavaScript类型转换的问题

( [ ][ [] ] + [ ] )[ -~{} ] + ( {} + {} )[ -~{} - ~{} ]

这篇文章主要通过一条完全由符号组成的语句来分析JavaScript是如何执行的。

首先,将式子分为两部分:

 1. ( [][ [] ] + [])[ -~{}] 
 2. ( {} + {} )[ -~{} - ~{} ]

第一部分

先来看第一部分。

  这一部分从总体看是一个通过下标访问特定的位置的表达式,在这里简写为

( X ) [ index ]

其中

  • X: [ ][ [] ]+ [ ]
  • index:-~{}
先看X:

X在这里分为两部分,声明两个标识符lvalrval,令

  • lval => [ ][ [] ]
  • rval => [ ]

分析AST树可以看出,这两部分都是表达式,并且通过二元操作符"+"连接。
查阅ECMAScript规范关于The Addition Operator一节的内容。

  1. lval+rval,实际将转换为ToPrimitve(lval)+ToPrimitve(rval)

  2. 对于lval,它是一个通过下标访问数组元素的表达式,其中数组为空数组,下标为“[]”。根据规范Integer Indexed Exotic Objects一节内容,数组的索引必须是整数值。

  3. 因此要将[]转换为数值类型。首先调用规范方法ToNumber,对[]进行转换。即ToNumber([])。
    在这里插入图片描述

  4. 查阅规范,因为[]不是基本类型,所以先调用ToPrimitive,转换为基本类型后再调用ToNumber
    在这里插入图片描述

  5. 查阅ToPrimitvie,对于数组类型,会先调用数组的valueOf方法,如果不能返回基本类型,再接着调用toString方法。如果都失败,会抛出TypeError错误。

  6. 在这里插入图片描述
      对于数组,因为调用valueOf返回原数组本身,所以接着调用toString方法。

      查阅 Array.prototype.toString ( ),该方法对调用Array.prototype.join ( separator )方法,将数组拼接成字符串,在这里传入的拼接符separator 参数为空,取默认值为","。
    在这里插入图片描述
       根据规范关于Array.prototype.join ( separator )的内容,“[]”是一个空数组,该方法的结果返回空字符串"",长度为0。

       因此ToPrimitve([])返回空字符串""

  7. 将 [] 转换为基本类型后,接着需要对ToPrimitve([])的返回值调用ToNumber,根据规范ToNumber("")返回 0。
       所以lval转换成 [ ][0],对于数组访问规则,因为[]是一个空数组,所以对任何位置的访问都返回undefined。

  8. 所以ToPrimitive(lval)的值为undefined。

  9. 接着看rval部分。通过上面对lval部分的求值可知,ToPrimitive([])返回值是""。

  10. 所以lval+rval,转换为undefined+"",根据二元操作符加法规则,当任何一方是sring类型时,操作符两边都将转换为string类型,所以undefined转换为string为“undefined”

lval+rval=“undefined”+""=“undefined”
即X部分结果为"undefined"

index部分:-~{}

index部分分为三个小块,

  1. 一元操作符: -
  2. 一元操作符: ~
  3. 表达式: {}

根据优先级,先求值~{},令其结果为rst1,再求值 -rst1

求值过程如下:

  1. 根据规范Bitwise NOT Operator ( ~ )一节内容,求值~{},实际会通过ToInt32 ( argument )方法将传入值转换为数值.在这里插入图片描述
  2. ToInt32 内部,首先要调用ToNumber将{}转换为Number类型。
  3. 根据上面提到过的流程,过程为ToPrimitve({}),再将其结果调用ToNumber
  4. 根据规范,ToPrimitive({}),实际会最终调用toString方法。根据Object.prototype.toString ( )一节内容,toString返回字符串"[object Object]",所以ToPrimitive({})值为字符串"[object Object]"
  5. 所以接着调用ToNumber("[object Object]"),根据规范,返回值为NaN。
  6. 根据ToInt32 方法描述,当ToNumber返回值为 NaN, +0, ‑0, +∞, 或者 ‑∞时, 一律返回+0.在这里插入图片描述
  7. 在这里,~{} 实际已经转换为~0。而~0结果为-1.所以~{}结果为-1
  8. 接着计算-(-1),返回1。
  9. 所以index部分的值为1

因此第一部分(X)[index]转换为(“undefined”)[1],是通过索引访问字符串的操作,返回单个字符"n"。

第二部分

下面再看第二部分

({} + {})[-~{} - ~{}]

这部分从总体看依旧是通过下标访问一个特定的位置,
在这里简写为

(Z)[index2]

其中

  • X:{} + {}
  • index:-~{} - ~{}
Z部分:

在这里需要注意,当{}作为语句的开头,它会优先被解释为语句块,而非对象表达式。
所以,我们可以看到{}+[]结果为0,正是因为如此。

  但在这里,因为Z位于括号里面,所以这里{}会被解释为表达式

  1. 首先根据二元操作符"+",操作符两侧的表达式会被调用ToPrimitive方法转换为基本值。
    即,ToPrimitve({})+ToPrimitve({})

  2. 根据上面已经得出的结论,ToPrimitve({})+ToPrimitve({})会被转换为:

    “[object Object]”+"[object Object]"

    所以{} + {} => “[object Object][object Object]”

  3. 因此Z部分值为"[object Object][object Object]"

index2部分:
  1. 根据上面已经得出的结论,~{}被转换为-1,所以-~{} - ~{}转换为-(-1)-(-1),值为2.

  2. 所以第二部分变为["[object Object][object Object]"][2],依旧是通过数字索引访问字符串特定字符,返回结果为"b"。

结论

结合第一部分和第一部分,上式变为"n"+“b”=>“nb”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值