likes
comments
collection
share

JS 中的隐式强制类型转换原理探究

作者站长头像
站长
· 阅读数 37

注意查看最新 es 规范,本文对照的是的 es7 标准(这个界面比 es6 的更易读):

262.ecma-international.org/7.0/#sec-to…

最新标准查看:

tc39.es/ecma262/

实际的类型转换结果和浏览器等宿主环境的支持规范的版本有关,规范迭代会导致某些规则或属性发生变换。

背景

做一个需求的过程中,疏忽了类型导致类型不匹配的报错。后续对输入进行强制类型转换解决了该问题。

JS 中的隐式强制类型转换原理探究

JS 中的隐式强制类型转换原理探究

不久前看了《你不知道的 JavaScript》,但其中还有一些似是而非的点比较模糊,所以正好借机重新整理一下。

JS 中强制类型转换有隐式显式两种类型

  • 显示转换需要调用构造函数,比较明显,如调用 Number,String
  • 隐式转换多见于 ==if 语句中,当你使用操作符对不同类型数据进行处理的时候会发生,不太明显。

相对于显式强制类型转换(以下称显示转换)能明显得出符合预期的类型,隐式强制类型转换(以下称隐式转换)似乎蒙着神秘的面纱,让人捉摸不透。本文着重介绍隐式转换。

为什么要写隐式类型转换?

当我们在意代码可读性、风格一致性、代码简洁性等情况下,需要这么处理。毕竟要向项目中存在的成熟的代码风格靠拢,另外在条件语句中判断空字符串操作确实比较常见,也比较推荐。

在生产开发过程中,需要避免使用很生僻的类型转换

隐式转换的要点:转换成相同类型

用例子说话

数组相加

+ 过程中会将对象(Object)转换成原始类型(Primitive),ab 都会被转化为原始类型。

        const a=[1,2];
        const b=[3,4];
        const res=a+b;
        console.log(res);
        // 操作数 a 是对象,对 a 调用 toPrimitive
        console.log('a.valueOf()',a.valueOf());// 还是数组 [1, 2]
        console.log('typeof a.valueOf()',typeof a.valueOf());// object
        console.log('a === a.valueOf()',a === a.valueOf());// true
        // 数组的 valueOf() 返回数组 [1, 2],无法得到简单基本类型,于是调用 toString
        console.log('a.toString()',a.toString());// 1,2
        console.log('typeof a.toString()',typeof a.toString());// string
        //数组的 toString() 返回字符串,结果就是两个字符串相加 
        console.log('a.toString()+b.toString()',a.toString()+b.toString());

JS 中的隐式强制类型转换原理探究

字符串和数组相加

+ 过程中会将对象(Object)转换成原始类型(Primitive),a 保持不变,b 从对象类型转换为原始类型。

        const a='word';
        const b=[1,2];
        const res = a + b;
        console.log(res);
        // 操作数 a 是字符串,直接返回原始参数
        // 操作数 b 是对象,对 b 调用 toPrimitive
        console.log('b.valueOf()', b.valueOf());// 还是数组 [1, 2]
        console.log('typeof b.valueOf()', typeof b.valueOf());// object
        console.log('b === b.valueOf()', b === b.valueOf());// true
        // 数组的 valueOf() 返回数组 [1, 2],无法得到简单基本类型,于是调用 toString
        console.log('b.toString()', b.toString());// 1,2
        console.log('typeof b.toString()', typeof b.toString());// string
        //数组的 toString() 返回字符串,结果就是两个字符串相加 
        console.log('a.toString()+b.toString()', a.toString() + b.toString());

JS 中的隐式强制类型转换原理探究

布尔相加

+ 调用 default 转换,最终调用 number 转换。

        const a=true;
        const b=false;
        const res = a + b;
        console.log(res);
        console.log('Number(a)', Number(a));// 1
        console.log('Number(b)', Number(b));// 0
        console.log('Number(a) + Number(b)', Number(a) + Number(b));// 1

JS 中的隐式强制类型转换原理探究

数字除以字符串

/ 调用 number 转换。这里换成两个字符串相除也是一样的效果。

        const a=12;
        const b='6';
        const res = a / b;
        console.log(res);
        console.log('Number(a)', Number(a));// 12
        console.log('Number(b)', Number(b));// 6
        console.log('Number(a) / Number(b)', Number(a) /Number(b));// 2

JS 中的隐式强制类型转换原理探究

字符串和布尔宽松等于

== 调用 default 转换,最终调用 number 转换。

        const a='true';
        const b=true;
        const res=a==b;
        console.log(res);
        console.log('Number(a)', Number(a));// 'NaN'
        console.log('typeof Number(a)',typeof Number(a));
        console.log('Number(b)', Number(b));// 1

JS 中的隐式强制类型转换原理探究

数组和 null 比较

JS 中的隐式强制类型转换原理探究

        const a=[1];
        const b=null;
        const res=a>b;
        console.log(res);
        console.log('a.toString()',a.toString());// "1"
        console.log('typeof a.toString()',typeof a.toString());// string
        console.log('Number',Number(b));// 0
        const c='1';
        const d=0;
        const res2=c>d;// true
        console.log(Number(c)>Number(d));// true
        console.log(Number(c)===c);// false
        console.log(Number(d)===d);// true

为了演示效果,只设置一个数组元素,不然会出现 NaN 的情况。

JS 中的隐式强制类型转换原理探究

ToString、ToNumber 操作过程中会涉及 toPrimitive

ToStringToNumber 对 Object 的转换会涉及到抽象操作 toPrimitive,是重点。除此之外,简单提一下这两类操作中的一些特性。

ToString:262.ecma-international.org/7.0/#sec-to…

ToNumber:262.ecma-international.org/7.0/#sec-to…

JS 中的隐式强制类型转换原理探究JS 中的隐式强制类型转换原理探究

查看对象的种类一般通过 Object.prototype.toString() 来查看。同时,它可以通过 toStringTag 改写。

JS 中的隐式强制类型转换原理探究JS 中的隐式强制类型转换原理探究

    function Foo() { }
    const a = new Foo();
    console.log(a.toString());// [object Object]
    
    Foo.prototype[Symbol.toStringTag] = 'Foo';
    console.log(a.toString());// [object Foo]

抽象操作 toPrimitive

262.ecma-international.org/7.0/#sec-to…

总的来说,转换的主要步骤如下

  1. 调用 obj[Symbol.toPrimitive](hint) 如果这个方法存在,
  1. 否则,如果 hintstring

    1. 尝试调用 obj.toString()obj.valueOf(),无论哪个存在。
  1. 否则,如果 hintnumber

    1. 尝试调用 obj.valueOf()obj.toString(),无论哪个存在。
  1. 如果 valueOf()toString() 均不返回基本类型值,会产生 TypeError 错误。

抽象操作 ToPrimitive 接受一个输入参数和一个可选的参数 PreferredType。抽象操作 ToPrimitive 将其输入参数转换为一个非对象类型。如果一个对象能够转换到一个以上的原始类型,它可以使用可选的提示 PreferredType 来选择该类型。分为两步。

JS 中的隐式强制类型转换原理探究

  1. ToPrimitive(input [, PreferredType])

主要执行步骤

JS 中的隐式强制类型转换原理探究

  1. OrdinaryToPrimitive(input, hint)

主要执行步骤

JS 中的隐式强制类型转换原理探究

hint 的判断方式

  • preferredType 参数的帮助下,不同的操作符可以触发 numberstring 转换。但有两个例外:== 和二进制 + 运算符会触发 default 的转换模式(没有指定首选类型,或等于 default)。
  • 大多数内置类型都假定 number转换是默认的(Date 默认 string 转换)

比如在把变量作为键值使用的时候,会调用ToPrimitive把键值转化为原始数据类型,并且PreferredType的值是hint string

调用 >,>= 时,PreferredTypehint number

更多情况可以参考红宝书以及一些网上资源都有总结。

JS 中的隐式强制类型转换原理探究

JS 中的隐式强制类型转换原理探究

定义@@toPrimitive

设置 hint

这里以 Date 中覆盖 toPrimitive 举例,Date 类型 hint 的默认值是 string,与相对于其他内建 ECMAScript 对象不同。

JS 中的隐式强制类型转换原理探究

  • 设置 hint default

JS 中的隐式强制类型转换原理探究

  • 设置 hintstring

JS 中的隐式强制类型转换原理探究

  • 设置 hintnumber

JS 中的隐式强制类型转换原理探究

  • 设置 hint 为无效类型

JS 中的隐式强制类型转换原理探究JS 中的隐式强制类型转换原理探究JS 中的隐式强制类型转换原理探究

改写 toString 和 valueOf
        const a = [1, 2]
        a.toString = function () {
            return 'origin is array'
        }
        const res='the ' + a ;
        console.log(res);

JS 中的隐式强制类型转换原理探究

       const a = [1]
        a.valueOf = function () {
            return 'origin is array'
        }
        console.log('a.valueOf()',a.valueOf());
        console.log('a.toString()',a.toString());
        console.log('typeof a.toString()',typeof a.toString());
        const arr=[];
        arr[a]='firstElement'
        console.log(arr)

JS 中的隐式强制类型转换原理探究

参考链接

录屏tech.bytedance.net/videos/7160…
和 &&zh.javascript.info/logical-ope…
ToPrimitive 和 OrdinaryToPrimitivesegmentfault.com/a/119000001…
类型转换教程www.freecodecamp.org/news/js-typ…
www.youtube.com/watch?v=wFi…
为 Date 设置 hintwww.geeksforgeeks.org/javascript-…
总结部分zh.javascript.info/object-topr…
转载自:https://juejin.cn/post/7157663823837528095
评论
请登录