神奇的JavaScript弱等价类型转换
JavaScript语言特性 - 类型转换
JavaScript这门语言的类型系统从来没有它表面看起来的那样和善,虽然比起Java、C#等一众强类型语言,它的弱类型使用起来似乎是如此便利,但正因为它极高的自由度,所以才会衍生出令人摸不着头脑的荒诞行为。
举个例子,虽然我们都知道一个包含内容的字符串会被认为是“真值 Truthy”(因为除了空字符串之外任何字符串在JS里都被认为真值),但当你做如下比较的时候,你会得到一个惊掉下巴的结果
const a = "18";
const b = true;
a == b // false
什么鬼,一个被通常理解成真值的值,竟然无法与布尔真值松散相等?
为了能拨开JavaScript类型的迷雾,让头铁的我们一点一点理顺JavaScript整个类型系统的工作逻辑。
读者可以根据自己对JS类型系统的掌握程度,选择性的阅读这篇博客
类型基础
JavaScript有以下八大类型,除了object类型,其他都为基本类型
numberstringbooleannullundefinedobjectsymbolbigint
他们的类型都可以直接被typeof识别,特例是
typeof null为"object"虽然它是null类型的值typeof function(){}为"function"虽然它理论上是object类型的
虽然你可能已经为这种特例所不解,但其实这才刚开始,大的还在后面
类型转换
转换为数字
JavaScript内部有一套抽象的数字转换机制叫ToNumber,这套机制在隐式转换或者部分显式转换其他类型值到数字时会被调用。虽然你可能会被恶心到,但我还是要向你介绍这套机制的规则为
string若为数字表达式则转换为其对应数字,否则返回NaNundefined转换为NaNnull转换为0true转换为数字1false转换为数字0object类型会依次调用toPrimitive()、valueOf()和toString()来获取值,并利用上面的规则获取其数字值
ToNumber的转换规则会在以下情况下使用,当然这些情况也可以称作转换数字的“技巧”,看你怎么理解它了:
Number(value)+ valueMath.floor(value)value * x,将一个值做乘法运算
没错,
Math.floor(true)、+ true、true * 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里对真假值的定义其实很简单,以下的值均为假值:
undefinednullfalse+0、-0、0n和NaN""
其他均为真值,任何非空字符串、非0数字、对象都是真值。
转换其他类型值为布尔类型的方法:
!!valueBoolean(value)!,可将值转换为布尔类型,但是真假结果相反
会隐式的将其他类型值转换为布尔的情况:
&&||if (value)while (value)for (...; value; ...),for循环的第二个测试表达式? :,三元操作符
恭喜你勇士,读到这里就代表马上你就能知道为什么"18" != true了!!!
等价性
我们都知道,JS当中有松散判断的弱等价==,和严格判断的强等价===两种判断等价方式。强等价要求两个操作值必须为同一类型,且值本身也相等,其行为非常容易预测。弱等价在比较值是否相等前会尝试做一些类型转换,尽可能的让可能为不同类型的两个值变得可以判断。
造成文章开头神奇判断结果的原因就在弱等价==时的类型转换策略上,听我将弱等价的转换规则为你娓娓道来
- 数字与字符串比较,则将字符串转换为数字
- 布尔值与任何其他值作比较,都先将布尔值转换为数字
null与undefined松散比较结果相等- 对象与数字或字符串比较,先调用对象的
toPrimitive()获取原始类型值后进行比较
所以,"18" == true进行比较时
- 由于布尔值的比较规则为将布尔值转换为数字,
ToNumber(true)结果为数字1,表达式变为"18" == 1 - 又因为字符串与数字比较时字符串应转换为数字,则
ToNumber("18")结果为数字18 - 最后表达式实际比较
18 == 1,结果为假false
所以没错,以下表达式均为真:
"1" == true,"0" == false,false == ""
结语
了解这个例子后你可能会对JavaScript语言行为设计混乱表达不满,但是上述边界条件可以通过引入TypeScript,避免弱等价判断转而使用严格等价,在隐式类型转换之前使用可预测行为的转换方法对值提前进行处理。
如果你无法采用上面列举的解决办法,那我的建议就是多多关注我的博客,如果觉得不错可以推荐给你的朋友或同事,说不定就能在身边慢慢带动大家采用更令人舒心的写法嘞。
ps. 辛苦读者了,为大家的好奇心与耐心致敬。
神奇的JavaScript弱等价类型转换的更多相关文章
- JavaScript中数据类型转换总结
JavaScript中数据类型转换总结 在js中,数据类型转换分为显式数据类型转换和隐式数据类型转换. 1, 显式数据类型转换 a:转数字: 1)Number转换: 代码: var a = " ...
- JavaScript内的类型转换
JavaScript内的类型转换 1.分为自动转换和强制转换,我们一般用强制转换.其他类型转换为整数是parseInt();其他类型转化为小数parseFloat(); 2.判断是不是一个合法数字 ...
- JavaScript 隐式类型转换
JavaScript 隐式类型转换 原文:https://blog.csdn.net/itcast_cn/article/details/82887895 · 1.1 隐式转换介绍 · 1.2 隐式转 ...
- javascript 六种基本数据类型转换
javascript 六种基本数据类型转换 1.显式转换 通过手动进行类型转换,Javascript提供了以下转型函数: 转换为数值类型:Number(mix).parseInt(string,rad ...
- 有趣的JavaScript隐式类型转换
JavaScript的数据类型是非常弱的(不然不会叫它做弱类型语言了)!在使用算术运算符时,运算符两边的数据类型可以是任意的,比如,一个字符串可以和数字相加.之所以不同的数据类型之间可以做运算,是因为 ...
- JavaScript中的类型转换(一)
前言 JavaScript是一种非常灵活的弱类型的语言,它的灵活性的一方面体现在其繁杂多样的类型转换.比如当JavaScript期望使用一个布尔值的时候(比如if语句中)你可以提供任一类型的值,Jav ...
- JavaScript的数据类型转换
首先,由于JavaScript是弱类型语言(弱类型的语言的东西没有明显的类型,他能随着环境的不同,自动变换类型而强类型则没这样的规定,不同类型间的操作有严格定义,只有相同类型的变量才能操作,虽然系统也 ...
- JavaScript 笔记(2) -- 类型转换 & 正则表达 & 变量提升 & 表单验证
目录: typeof, null, undefined, valueOf() 类型转换 正则表达式 错误: try, catch, throw 调试工具 变量提升 strict 严格模式 使用误区 ...
- JavaScript显式类型转换与隐式类型转换
隐式类型转换 四则运算 判断语句 toString 在 JavaScript 中声明变量不需指定类型,对变量赋值也没有类型检查,同时还允许隐式类型转换. 这些特征说明 JavaScript 属于弱类型 ...
- javascript中的类型转换,宽松相等于严格相等
为了将值转换为基本类型值(string,number,boolean,null,undefined),抽象操作ToPrimitive会首先检查该值有没有valueOf()方法,如果有并且返回基本类型值 ...
随机推荐
- 数据湖揭秘—Delta Lake
简介:Delta Lake 是 DataBricks 公司开源的.用于构建湖仓架构的存储框架.能够支持 Spark,Flink,Hive,PrestoDB,Trino 等查询/计算引擎.作为一个开放 ...
- 阿里云CDN操控2.0版本正式发布
简介: 2021年8月,阿里云边缘云CDN完成过去3年来最大的一次版本升级. 2021年8月,阿里云边缘云CDN完成过去3年来最大的一次版本升级.本次升级根据上万企业客户的使用反馈和行业应用特征,从 ...
- 从右边开始寻找整数的第k位
从右边开始寻找整数的第k位 Implement match_k, which takes in an integer k and returns a function that takes in a ...
- 解决 System.Net.Sockets.SocketException 10106 无法加载或初始化请求的服务提供程序 无法联网
本文收集 System.Net.Sockets.SocketException 异常错误码为 10106 导致无法联网的问题 这里的 10106 是 Win32 的 Socket 错误码,可以从 Wi ...
- 2019-8-31-C#-简单读取文件
title author date CreateTime categories C# 简单读取文件 lindexi 2019-08-31 16:55:58 +0800 2018-07-19 16:48 ...
- await this.$nextTick()和this.$nextTick(callback)有什么区别?记一次bug调试
背景 需要实现一个需求,一个小区业务详情页面,在左侧菜单栏切换了小区后,详情页跟着切换. 这个详情页面是根据url上的/:id来确定小区id的,所以切换了小区后,应该切换路由. 于是这样实现: wat ...
- Codeforces Round 927 (Div. 3) EFG
E:Link 题意:给定长度小于 \(4 \times 10^5\) 的整数 \(n\),求从 \(0\) 到 \(n\) 各数位变化次数之和. 如:\(n = 12345\) 个位变化 \(1234 ...
- CF1872G
题意:一个正整数序列,\(a[i] < 10^9\),求 \(l\),\(r\),最大化 \[\sum_{i = 1}^{l - 1} a[i] + \prod_{i = l}^r a[i] + ...
- 羽夏看Linux内核——简述
写在前面 此系列是本人一个字一个字码出来的,包括示例和实验截图.如有好的建议,欢迎反馈.码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作.如想转载,请把我的转载信息附在文章后面,并 ...
- docker 搭建LNMP环境
php7 仓库地址 https://gitee.com/haima1004/docker-lnmp