JavaScript语言特性 - 类型转换

JavaScript这门语言的类型系统从来没有它表面看起来的那样和善,虽然比起Java、C#等一众强类型语言,它的弱类型使用起来似乎是如此便利,但正因为它极高的自由度,所以才会衍生出令人摸不着头脑的荒诞行为。

举个例子,虽然我们都知道一个包含内容的字符串会被认为是“真值 Truthy”(因为除了空字符串之外任何字符串在JS里都被认为真值),但当你做如下比较的时候,你会得到一个惊掉下巴的结果

const a = "18";
const b = true; a == b // false

什么鬼,一个被通常理解成真值的值,竟然无法与布尔真值松散相等?

为了能拨开JavaScript类型的迷雾,让头铁的我们一点一点理顺JavaScript整个类型系统的工作逻辑。

读者可以根据自己对JS类型系统的掌握程度,选择性的阅读这篇博客

类型基础

JavaScript有以下八大类型,除了object类型,其他都为基本类型

  • number
  • string
  • boolean
  • null
  • undefined
  • object
  • symbol
  • bigint

他们的类型都可以直接被typeof识别,特例是

  • typeof null"object"虽然它是null类型的值
  • typeof function(){}"function"虽然它理论上是object类型的

虽然你可能已经为这种特例所不解,但其实这才刚开始,大的还在后面

类型转换

转换为数字

JavaScript内部有一套抽象的数字转换机制叫ToNumber,这套机制在隐式转换或者部分显式转换其他类型值到数字时会被调用。虽然你可能会被恶心到,但我还是要向你介绍这套机制的规则为

  • string若为数字表达式则转换为其对应数字,否则返回NaN
  • undefined转换为NaN
  • null转换为0
  • true转换为数字1
  • false转换为数字0
  • object类型会依次调用toPrimitive()valueOf()toString()来获取值,并利用上面的规则获取其数字值

ToNumber的转换规则会在以下情况下使用,当然这些情况也可以称作转换数字的“技巧”,看你怎么理解它了:

  • Number(value)
  • + value
  • Math.floor(value)
  • value * x,将一个值做乘法运算

没错,Math.floor(true)+ truetrue * 1都等于1,是不是觉得很荒诞?

'5' + 3或者5 + '3'结果都是字符串'53',因为+在有两个操作值,且其中一个为字符串时,会直接做字符串拼接。所以虽然+ '3'结果为数字3,但5 + '3'的结果不是8

parseInt()parseFloat()只接受string类型,所以转换规则与ToNumber转换机制下的string类型情况相似,但是在处理字符时采取从左到右的扫描直到失败为止的方法,所以parseInt("123hello")结果为123

转换为布尔

JavaScript还有一套针对布尔类型的抽象转换机制叫ToBoolean。因为对于前端逻辑编写来讲,判断一个值是否为真实在太重要了,JavaScript里变量像薛定谔的猫一样,处于存在与不存在、真和假的中间态,所以我们JS开发者都有一个奇怪的脑回路,当看到一个字面量值的时候就开始评估它是“真值”(Trusy Value)还是“假值”(Falsy Value)。

可是,与物理学里薛定谔的猫现象相反,JavaScript里对真假值的定义其实很简单,以下的值均为假值

  • undefined
  • null
  • false
  • +0-00nNaN
  • ""

其他均为真值,任何非空字符串、非0数字、对象都是真值。

转换其他类型值为布尔类型的方法:

  • !!value
  • Boolean(value)
  • !,可将值转换为布尔类型,但是真假结果相反

会隐式的将其他类型值转换为布尔的情况:

  • &&
  • ||
  • if (value)
  • while (value)
  • for (...; value; ...),for循环的第二个测试表达式
  • ? :,三元操作符

恭喜你勇士,读到这里就代表马上你就能知道为什么"18" != true了!!!

等价性

我们都知道,JS当中有松散判断的弱等价==,和严格判断的强等价===两种判断等价方式。强等价要求两个操作值必须为同一类型,且值本身也相等,其行为非常容易预测。弱等价在比较值是否相等前会尝试做一些类型转换,尽可能的让可能为不同类型的两个值变得可以判断。

造成文章开头神奇判断结果的原因就在弱等价==时的类型转换策略上,听我将弱等价的转换规则为你娓娓道来

  • 数字与字符串比较,则将字符串转换为数字
  • 布尔值与任何其他值作比较,都先将布尔值转换为数字
  • nullundefined松散比较结果相等
  • 对象与数字或字符串比较,先调用对象的toPrimitive()获取原始类型值后进行比较

所以,"18" == true进行比较时

  • 由于布尔值的比较规则为将布尔值转换为数字,ToNumber(true)结果为数字1,表达式变为"18" == 1
  • 又因为字符串与数字比较时字符串应转换为数字,则ToNumber("18")结果为数字18
  • 最后表达式实际比较18 == 1,结果为假false

所以没错,以下表达式均为真:"1" == true"0" == falsefalse == ""

结语

了解这个例子后你可能会对JavaScript语言行为设计混乱表达不满,但是上述边界条件可以通过引入TypeScript,避免弱等价判断转而使用严格等价,在隐式类型转换之前使用可预测行为的转换方法对值提前进行处理。

如果你无法采用上面列举的解决办法,那我的建议就是多多关注我的博客,如果觉得不错可以推荐给你的朋友或同事,说不定就能在身边慢慢带动大家采用更令人舒心的写法嘞。

ps. 辛苦读者了,为大家的好奇心与耐心致敬。

神奇的JavaScript弱等价类型转换的更多相关文章

  1. JavaScript中数据类型转换总结

    JavaScript中数据类型转换总结 在js中,数据类型转换分为显式数据类型转换和隐式数据类型转换. 1, 显式数据类型转换 a:转数字: 1)Number转换: 代码: var a = " ...

  2. JavaScript内的类型转换

    JavaScript内的类型转换 1.分为自动转换和强制转换,我们一般用强制转换.其他类型转换为整数是parseInt();其他类型转化为小数parseFloat(); 2.判断是不是一个合法数字   ...

  3. JavaScript 隐式类型转换

    JavaScript 隐式类型转换 原文:https://blog.csdn.net/itcast_cn/article/details/82887895 · 1.1 隐式转换介绍 · 1.2 隐式转 ...

  4. javascript 六种基本数据类型转换

    javascript 六种基本数据类型转换 1.显式转换 通过手动进行类型转换,Javascript提供了以下转型函数: 转换为数值类型:Number(mix).parseInt(string,rad ...

  5. 有趣的JavaScript隐式类型转换

    JavaScript的数据类型是非常弱的(不然不会叫它做弱类型语言了)!在使用算术运算符时,运算符两边的数据类型可以是任意的,比如,一个字符串可以和数字相加.之所以不同的数据类型之间可以做运算,是因为 ...

  6. JavaScript中的类型转换(一)

    前言 JavaScript是一种非常灵活的弱类型的语言,它的灵活性的一方面体现在其繁杂多样的类型转换.比如当JavaScript期望使用一个布尔值的时候(比如if语句中)你可以提供任一类型的值,Jav ...

  7. JavaScript的数据类型转换

    首先,由于JavaScript是弱类型语言(弱类型的语言的东西没有明显的类型,他能随着环境的不同,自动变换类型而强类型则没这样的规定,不同类型间的操作有严格定义,只有相同类型的变量才能操作,虽然系统也 ...

  8. JavaScript 笔记(2) -- 类型转换 & 正则表达 & 变量提升 & 表单验证

    目录:  typeof, null, undefined, valueOf() 类型转换 正则表达式 错误: try, catch, throw 调试工具 变量提升 strict 严格模式 使用误区 ...

  9. JavaScript显式类型转换与隐式类型转换

    隐式类型转换 四则运算 判断语句 toString 在 JavaScript 中声明变量不需指定类型,对变量赋值也没有类型检查,同时还允许隐式类型转换. 这些特征说明 JavaScript 属于弱类型 ...

  10. javascript中的类型转换,宽松相等于严格相等

    为了将值转换为基本类型值(string,number,boolean,null,undefined),抽象操作ToPrimitive会首先检查该值有没有valueOf()方法,如果有并且返回基本类型值 ...

随机推荐

  1. 百万TPS高吞吐、秒级低延迟,阿里​搜索离线平台如何实现?

    导读:阿里主搜(淘宝天猫搜索)是搜索离线平台非常重要的一个业务,具有数据量大.一对多的表很多.源表的总数多和热点数据等特性.对于将主搜这种逻辑复杂的大数据量应用迁移到搜索离线平台总是不缺少性能的挑战, ...

  2. CDN应用进阶 | 正确使用CDN 让你更好规避安全风险

    为了帮助用户更好地了解和使用CDN产品,CDN应用实践进阶系统课程开课了.12月17日,阿里云CDN产品专家彭飞在线分享了<正确使用CDN,让你更好规避安全风险>议题,内容主要包括以下几个 ...

  3. GitHub 的 Action 接入 Stryker.NET 进行自动化测试单元测试鲁棒性

    假设有一个捣蛋的小伙伴加入了你的团队,这个捣蛋的小伙伴喜欢乱改代码,请问此时的单元测试能否拦住这些逗比行为?如果不能拦住逗比行为,是否代表着单元测试有所欠缺,或者有某些分支逻辑没有考虑到.本文将告诉大 ...

  4. 18.基于Consul的服务发现和ConsulManager管理

    192.168.10.14 prometheus.consul 192.168.10.100 各类服务 一.基于Consul的服务发现 Consul 是由 HashiCorp 开发的一个支持多数据中心 ...

  5. K8s包管理工具Helm v3(19)

    一.Helm概述 官网:https://v3.helm.sh/zh/docs/ https://helm.sh/ helm 官方的 chart 站点: https://hub.kubeapps.com ...

  6. Ubuntu VNC 远程桌面及常见问题

    一.Ubuntu 远程桌面开启 在ubuntu 设置中打开远程桌面 **注意:如果没有共享桌面选项也不要谎,只需要安装 vino 即可 sudo apt update sudo apt install ...

  7. 07. C语言程序执行流程控制

    [有条件执行语句] if esle 语句 if else 语句根据一个条件确定是否执行一段代码,执行条件是一个布尔值,布尔值为true则执行,为false则不执行,同时可以设置不符合条件时执行的语句. ...

  8. python教程3.2:字典

    字典相比较列表,优势:查找key的需求,列表需要遍历,字典查找速度很快,很方便, 定义  特性 查找.增加和修改操作  删除操作 循环操作  全局函数

  9. postgresql性能优化3:分区表

    一.分区表产生的背景 随着使用时间的增加,数据库中的数据量也不断增加,因此数据库查询越来越慢. 加速数据库的方法很多,如添加特定的索引,将日志目录换到单独的磁盘分区,调整数据库引擎的参数等.这些方法都 ...

  10. uniapp微信小程序uni.request捕获500异常

    通常使用ajax,axios等进行服务请求,500错误或者其他的错误都会直接进入到错误通道里头,比如ajax异常的话会进入到error的回调函数里头,axios异常会进行到catch里头,一开始以为u ...