神奇的JavaScript弱等价类型转换

javascript · 浏览次数 : 0

小编点评

本文将探讨JavaScript中类型转换的相关问题,包括类型系统的基础知识、类型转换规则以及如何避免弱等价比较带来的困惑。 首先,文章介绍了JavaScript的八大基本类型: number、string、boolean、null、undefined、object、symbol 和 bigint。这些类型可以直接通过typeof操作符识别。特别地,文章提到了typeof null返回的是"object",这是因为null在JavaScript中被视为一个特殊的对象。 接下来,文章详细解释了JavaScript内部的数字转换机制,称为ToNumber。这个机制会在隐式转换或部分显式转换其他类型值到数字时被调用。文章给出了ToNumber的转换规则,以及在哪些情况下它会使用这些规则。例如,当字符串被转换为数字时,如果字符串可以被认为是有效的数字表达式,则会将其转换为相应的数字;否则,结果将为NaN。 文章还讨论了JavaScript的布尔类型转换机制,称为ToBoolean。这个机制会根据值的真假来将其转换为布尔值。文章举例说明了如何在表达式中使用这些转换规则,以避免弱等价比较中的类型转换问题。 最后,文章讨论了JavaScript中的等价性概念,包括松散等价(==)和严格等价(===)。文章指出了弱等价比较的行为,并建议在使用JavaScript时尽量避免这种比较,而是使用严格等价比较以确保值的相等性。 总结来说,本文通过分析JavaScript的类型系统和转换机制,帮助读者更好地理解JavaScript语言的行为,并提供了一些避免类型转换引发问题的建议。

正文

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弱等价类型转换相似的内容:

神奇的JavaScript弱等价类型转换

JavaScript语言特性 - 类型转换 JavaScript这门语言的类型系统从来没有它表面看起来的那样和善,虽然比起Java、C#等一众强类型语言,它的弱类型使用起来似乎是如此便利,但正因为它极高的自由度,所以才会衍生出令人摸不着头脑的荒诞行为。 举个例子,虽然我们都知道一个包含内容的字符串会

带你揭开神秘的javascript AST面纱之AST 基础与功能

在前端里面有一个很重要的概念,也是最原子化的内容,就是 AST ,几乎所有的框架,都是基于 AST 进行改造运行,比如:React / Vue /Taro 等等。 多端的运行使用,都离不开 AST 这个概念。在大家理解相关原理和背景后,我们可以通过手写简单的编译器,简单实现一个 Javascript 的代码编译器,编译后在浏览器端正常运行。

带你揭开神秘的Javascript AST面纱之Babel AST 四件套的使用方法

对 AST 有了初步的认识,还有常规的代码改造应用实践,现在我们来详细说说使用 AST, 如何进行代码改造?

JS神奇的或0(|0)

按照常识,位运算x|0,要么等于x,要么等于0 那么在JS的世界你的认知就要被颠覆了 下面请看 不带或0运算: (window.crypto.getRandomValues(new Uint32Array(1))[0] * 0x10000 ) 168546249998336 (window.cryp

【动画进阶】神奇的背景,生化危机4日食 Loading 动画还原

最近,在 Steam 玩一款老游戏(生化危机 4 重置版),其中,每当游戏转场的过程中,都有这么一个有趣的 Loading 动画: 整个效果有点类似于日食效果,中间一圈黑色,向外散发着太阳般的光芒。 本文,我们将尝试使用 CSS,还原这个效果。 整个效果做出来,类似于如下两个动画效果这样: 实现主体

JS语法让人困惑的点 “==与===”

在JS中有很多神奇的语法,非常让人困惑,我们就先一一道来,相信你在开发中或多或少都踩过这些坑,或者让人无法理解。 今天我们就来说下 "`==`" 和 "`===`" 这题对于很多没有系统学过前端开发的技术人员来说,算个重点,来画起来,我们一起看。

算法金 | Transformer,一个神奇的算法模型!!

大侠幸会,在下全网同名「算法金」 0 基础转 AI 上岸,多个算法赛 Top 「日更万日,让更多人享受智能乐趣」 抱个拳,送个礼 在现代自然语言处理(NLP)领域,Transformer 模型的出现带来了革命性的变化。它极大地提升了语言模型的性能和效率,而自注意力机制是其中的核心组件。 今个儿我们将

[转帖]Skip List--跳表(全网最详细的跳表文章没有之一)

https://www.jianshu.com/p/9d8296562806 跳表是一种神奇的数据结构,因为几乎所有版本的大学本科教材上都没有跳表这种数据结构,而且神书《算法导论》、《算法第四版》这两本书中也没有介绍跳表。但是跳表插入、删除、查找元素的时间复杂度跟红黑树都是一样量级的,时间复杂度都是

微软的148座坟墓

深夜档分享,给大家介绍一个黑白的、“惊悚”的网站! 从名字来看(killed by microsoft),是不是猜到点端倪了? 这个神奇的网站居然收录了微软寿终正寝的那些软件。这是一个免费的开放源码列表,其中列出了已停产的微软服务、产品、设备和应用程序。网站的目标是成为有关微软已死项目历史的真实信息

使用 shell 脚本自动申请进京证 (六环外) —— debug 过程

写好的自动办理六环外进京证脚本跑不通,总是返回办理业务人数较多 (500) 错误,Charles / VNET 抓包、android 交叉编译 jq、升级 curl…都不起作用,最终还是神奇的 adb shell 帮了大忙,最后定位到根因,居然是用 shell 字符串长度作为数据长度导致的,这错误犯的有点低级……