最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • JavaScript 类型转换不完全记录

    正文概述 掘金(afishhhhh)   2021-01-15   510

    这篇文章主要记录从对象转换为 Primitive Value 的过程,至于基本类型之间的转换,可以看看其他的文章,或者直接去看规范。 首先,我们需要知道,下文中会出现的一些函数,比如 ToObjectToStringToNumber 以及 ToPrimitive 等等,都是在规范中实现的函数,我们没有办法直接通过 JavaScript 访问到。

    valueOf

    一般情况下,所有的对象都可访问到 valueOf 方法,比如 Array,虽然自身没有 valueOf 方法,但是可以根据原型链从 Object.prototype 中找到。

    Object 原型上的 valueOf 方法会调用规范中的 ToObject(argument) 函数。顾名思义,这个函数会返回一个对象。如果 argument 本身就是对象,就返回自身;如果是 nullundefined 则抛错;像 booleanstringnumber 以及 symbol 这样的基本类型,就会返回对应的包装对象。相反,如果这些包装类型调用 valueOf 方法,则会返回对应的基本类型值,并不是返回自身,因为这些包装类型都实现了自己的 valueOf 方法。

    我们可以将 ToObject() 大致类比为 Object(),只不过后者在处理 nullundefined 的时候会返回 {}

    toString

    提一下 Number.prototype.toString 以及 Array.prototype.toString 吧。

    • Number.prototype.toString 接受一个参数 radix,可以是二进制,八进制等等,当然默认是十进制。

    • Array.prototype.toString 内部调用的是 join 方法,join 的实现在这里

    ToPrimitive(input[, preferredType])

    简单讲 ToPrimitive 会将一个值转换为 Primitive Value。在梳理 ToPrimitive 的执行过程前,我们先了解一个内置的 Symbol

    • Symbol.toPrimitive

      这个值可以作为对象的属性名,指向一个方法,用于控制对象如何转换为 Primitive Value。这个方法会在 ToPrimitive 的执行过程中使用到。并不是所有内置对象都有这个属性的,只有 Date.prototype 以及 Symbol.prototype 存在这个属性。虽然普通对象上没有这个属性,但我们是可以手动添加上这个属性,比如:

      var o = {
        [Symbol.toPrimitive]: function(hint) {}
      }
      

    下面就梳理下 ToPrimitive 的执行过程:

    1. 如果 input 的类型是 object

      a. 声明一个变量 hint,如果 preferredType 不存在,将 hint 赋值为 default

      b. 如果 preferredType 为 stringnumber,将 hint 赋值为 stringnumber

      c. 判断 input 是否存在 Symbol.toPrimitive 属性,如果存在则调用该属性指向的方法

      • 如果返回值是 Primitive Value,则返回该值;否则抛错

      d. 不存在该方法,当 hintdefault 是,重新赋值为 number

      e. 如果 hintstring,则按顺序调用 input 的 toString 以及 valueOf 方法,直到返回 Primitive Value

      f. 如果 hintnumber,则按顺序调用 input 的 valueOf 以及 toString 方法,直到返回 Primitive Value

      g. 如果最终没有返回 Primitive Value,则抛错

    2. input 本身就是一个 Primitive Value,直接返回 input

    其中步骤 e-f 对应的是规范中的 OrdinaryToPrimitive 函数

    ToNumber(argument)

    当尝试把一个对象转换为数字时,会有以下两个步骤:

    1. 调用 ToPrimitive(input, Number),返回值为 primValue

    2. 调用 ToNumber(primValue)

    比如我们会用到的 Number(value),就会使用到 ToNumber 这个内部函数(前提是你传了一个参数,不然的话就直接返回 0 了)。另外,一元 + 运算也相当于 Number(value)

    举个例子:Number({}) // NaN

    1. {} 作为 argument,调用 ToNumber(argument)

    2. {} 作为 input,调用 ToPrimitive(input, Number)hint 被赋值为 number

    3. 因为 {} 不存在 Symbol.toPrimitive 属性,所以按顺序调用 valueOf 以及 toString

    4. 调用 valueOf 返回自身,不是 Primitive Value

    5. 调用 toString 返回 [object Object]

    6. [object Object] 返回,作为 primValue

    7. 调用 ToNumber(primValue) 返回 NaN

    简单讲:一般情况下,尝试将对象转换为数字时,会调用 valueOf 以及 toString,直到返回 Primitive Value

    ToString(argument)

    ToNumber 类似,当尝试把对象转换为字符串时,也会有两个步骤:

    1. 调用 ToPrimitive(input, String),返回值为 primValue

    2. 调用 ToString(primValue)

    举个例子:String({}) // [object Object]

    1. {} 作为 argument,调用 ToString(argument)

    2. {} 作为 input,调用 ToPrimitive(input, String)hint 被赋值为 string

    3. 因为 {} 不存在 Symbol.toPrimitive 属性,所以按顺序调用 toString 以及 valueOf

    4. 调用 toString 返回 [object Object],是一个 Primitive Value

    5. [object Object] 返回,作为 primValue

    6. 调用 ToString(primValue) 返回 [object Object]

    简单讲:一般情况下,尝试将对象转换为字符串时,会调用 toString 以及 valueOf,直至返回 Primitive Value

    二元 + 运算

    我们知道 + 不仅能进行数学加法,又可以连接字符串。不仅 1 + '1' 可以执行,甚至像 null + 1[] + {} 等等运算都可以执行。我们可以根据规范梳理下二元 + 的运算过程,在这个过程中也用到了 ToPrimitive 函数:

    1. 对两个操作数调用 ToPrimitive(input),此时没有指定 preferredType,hint 会被赋值为 default

    2. 判断两个返回值的类型,如果其中有一个为 string

      a. 对两个返回值执行 ToString

      b. 进行字符串连接

    3. 对两个返回值执行 ToNumber,执行数学加法

    [] + {} 为例,分析一下:

    1. 执行 ToPrimitive([]),根据规则,会调用 [].toString(),返回值为 ''

    2. 执行 ToPrimitive({}),同样会调用 ({}).toString(),返回值为 [object Object]

    3. 进行字符串连接得到 [object Object]

    那么,如果手动改变了 valueOf 或者 toString 的行为呢,比如:

    var o1 = {
      valueOf: function () {
        return 1
      }
    }
    var o2 = {
      toString: function () {
        return 2
      }
    }
    

    当执行 o1 + o2 时,最终就是执行数学加法,结果为 3。

    == 运算

    根据规范,x == y 运算的执行过程如下:

    1. 如果 x 与 y 的类型相同,执行 x === y=== 执行步骤)

    2. 如果 x 与 y 中,其中一个为 undefined 另一个为 null,则返回 true

    3. 如果 x 与 y 中,其中一个为 string 另一个为 number,则返回 ToNumber(one) == another 的结果

    4. 如果 x 与 y 中,存在一个 boolean,则返回 ToNumber(one) == another 的结果

    5. 如果 x 与 y 中,其中一个为 object,另一个为任一 stringnumber 或者 symbol,则返回 ToPrimitive(one) == another 的结果

    6. 以上情况之外,返回 false

    我们以 [] == ![] 为例来分析这个过程:

    1. ![] 会调用 ToBoolean([]) 并取反,得到结果 false

    2. 比较 [] == false,根据上文步骤 4,得到 [] == 0

    3. 根据上文步骤 5,得到 '' == 0

    4. 根据上文步骤 3,得到 0 == 0

    5. 返回 true

    再看 Symbol.toPrimitive

    上文提到,在一般情况下,当对象转字符串或者数字时,会调用 valueOf 以及 toString。那么,除一般情况以外会是怎么样呢?

    回到 Symbol.toPrimitive 属性,我们现在手动为普通对象添加这个属性(上文说过,除了 Date.prototypeSymbol.prototype,其他对象都没有这个属性)。

    var o = {
      [Symbol.toPrimitive]: function (hint) {
        switch (hint) {
          case 'number':
            return 1
          case 'string':
            return 'str'
          case 'default':
            return 'default'
          default:
            throw new Error()
        }
      }
    }
    Number(o) // 1
    String(o) // 'str'
    o + 1     // 'default1'
    

    如果理解了上文的 ToPrimitive 函数,就可以知道:如果一个对象存在 Symbol.toPrimitive 属性,那么 valueOf 以及 toString 方法都不会被调用了

    最后

    总结,emmmm...全文都是总结


    起源地下载网 » JavaScript 类型转换不完全记录

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元