js中为什么你不敢用 “==”

更新日期: 2019-07-19 阅读: 2.4k 标签: 类型

前言

类型转换在各个语言中都存在,而在 JavaScript 中由于缺乏对其的了解而不慎在使用中经常造成bug被人诟病。为了避免某些场景下的意外,甚至推崇直接使用 Strict Equality( === )来代替 ==。这确实能避免很多bug,但更是一种对语言不理解的逃避(个人观点)。


引入

先抛出在 You Don’t Know JavaScript (中) 看到的一个例子

  [] == [] // false
  [] == ![] // true
  {} == !{} // false
  {} == {} // false

是不是很奇怪?本文将从书中看到的知识与规范相结合,来详细说明一下JavaScript在类型转换时候发生的故事。


类型转换

很多人喜欢说显示类型转换与隐式类型转换,但个人感觉只是说法上的不同,实质都在发生了类型转换而已,故不想去区分他们了(感觉一万个人有一万种说法)

仅在6大基本类型 null undefined number boolean string object 作讨论 symbol未考虑

 

举个栗子

var a = String(1)
var b = Number('1')
var c = 1 + ''
var d = +'1'

a,b直接调用了原生函数,发生了类型转换。c,d使用了+运算符的一些规则,发生了类型转换。这些是很简单的也是我们常用的。

其实真正起作用的,是语言内部对规范中抽象操作的实现,接下来我们所说的 ToString, ToNumber, ToBoolean等都是抽象操作,而不是JS里对应的内置函数


ToString – 规范9.8

按照以下规则转化被传递的参数

Argument TypeResult
Undefined“undefined”
Null“null”
Booleantrue -> “true”
false – > “false”
NumberNaN -> “NaN”
+0 -0 -> “0”
-1 -> “-1”
infinity -> “Infinity”
较大的数科学计数法 (详见规范9.8.1)
String不转换 直接返回
Object1. 调用ToPrimitive抽象操作, hint 为 String 将返回值作为 value
2. 返回ToString(value)
String(undefined) // "undefined"
String(null) // "null"
String(true) // "true"

ToPrimitive 抽象操作下面会提及


ToNumber – 规范9.3

按照以下规则转换被传递参数

Argument TypeResult
UndefinedNaN
Null+0
Booleantrue -> 1
false -> +0
Number直接返回
String如果不是一个字符串型数字,则返回NaN(具体规则见规范9.3.1)
Object1. 调用ToPrimitive抽象操作, hint 为 Number 将返回值作为 value
2. 返回ToNumber(value)

ToBoolean – 规范9.2

按照以下规则转换被传递参数

Argument TypeResult
Undefinedfalse
Nullfalse
Boolean直接返回
Number+0 -0 NaN -> false
其他为true
String空字符串(length为0) -> false
其他为true
Objecttrue

ToPrimitive – 规范9.1

顾名思义,该抽象操作定义了该如何将值转为基础类型(非对象),接受2个参数,第一个必填的要转换的值,第二个为可选的hint,暗示被转换的类型。

按照以下规则转换被传递参数

Argument TypeResult
Undefined直接返回
Null直接返回
Boolean直接返回
Number直接返回
String直接返回
Object返回一个对象的默认值。一个对象的默认值是通过调用该对象的内部方法[[DefaultValue]]来获取的,同时传递可选参数hint。

[[DefaultValue]] (hint) – 规范8.12.8

当传递的hint为 String 时候,
如果该对象的toString方法可用则调用toString
如果toString返回了一个原始值(除了object的基础类型)val,则返回val
如果该对象的valueOf方法可用则调用valueOf方法
如果valueOf返回了一个原始值(除了object的基础类型)val,则返回val
抛出TypeError的异常
当传递的hint为 Number 时候,
如果该对象的valueOf方法可用则调用valueOf方法
如果valueOf返回了一个原始值(除了object的基础类型)val,则返回val
如果该对象的toString方法可用则调用toString
如果toString返回了一个原始值(除了object的基础类型)val,则返回val
抛出TypeError的异常
hint的默认值为Number,除了Date object
举个栗子
var a = {}
a.toString = function () {return 1}
a.valueOf = function () {return 2}
String(a) // "1"
Number(a) // 2
a + '' // "2"   ???????
+a // 2
a.toString = null
String(a) // "2"
a.valueOf = null
String(a) // Uncaught TypeError: balabala

似乎我们发现了一个很不合规范的返回值,为什么 a + ''不应该返回”1″吗

问题的答案其实很简单 + 操作符会对两遍的值进行 toPrimitive 操作。由于没有传递 hint 参数,那么就会先调用a.valueOf 得到2后因为+右边是字符串,所以再对2进行ToString抽象操作后与””的字符串拼接。


不要畏惧使用 ==

基础概念已经了解了,那么在 == 中到底发生了什么样的类型转换,而导致了经常产生出乎意料的bug,导致了它臭名昭著。


抽象相等 – 规范11.9.3

x == y 判断规则如下:

    如果xy类型相同 (与严格相等判断一致,不赘述了,详见规范)
    如果 x 为 null y 为 undefined, 返回true
    如果 x 为 undefined y 为 null, 返回true
    如果 x 类型为 Number, y 类型为 String, 返回 x == ToNumber(y)
    如果 x 类型为 String, y 类型为 Number, 返回ToNumber(x) == y
    如果 x 类型为 Boolean, 返回 ToNumber(x) == y
    如果 y 类型为 Boolean, 返回 x == ToNumber(y)
    如果 x 类型为 String 或 Number, y 类型为 Object, 返回 x == ToPrimitive(y)
    如果 x 类型为 Object, y 类型为 String 或 Number, 返回 ToPrimitive(x) == y
    return false

再看引入

  [] == [] // false
  // 1. 两遍类型都为 Object,比较引用地址,不同返回false 搞定
  [] == ![] // true
  // 1. ![]强制类型转换 变为 [] == false
  // 2. 根据规范第7条,返回 [] == ToNumber(false), 即 [] == 0
  // 3. 根据规范第9条,返回ToPromitive([]) == 0,数组的valueOf为本身,不是原始值,则返回toString()即 "" == 0
  // 4. 根据规范第5条,返回ToNumber("") == 0, 即 0 == 0
  // 5. 根据规范第1条,返回 true

  // 下面的不赘述了,分析类似上面
  {} == !{} // false
  {} == {} // false

我们不难看出以下几点

其实在x y类型相同的时候,== 与 === 没有任何区别。
除了undefined与null, 大多数值都会转换为相同类型后进行对比,也就是说 === 是 == 某些情况下必经的步骤

引用 << 你不知道的JS(中) >> 中的2句话

如果两遍的值中有 true 或者 false , 千万不要使用 == (会被转为数字0,1来进行判断,会出现一些意外的情况)
如果两遍的值中有[]、””或者0,尽量不要使用 ==


抽象比较

先来看看这个例子

var a = { b: 42 }
var b = { b: 43 }
a < b // false
a == b // false
a > b // false

a <= b // true
a >= b // true

是不是感觉到世界又崩塌了???

让我们来仔细分析一下

var a = { b: 42 }
var b = { b: 43 }
a < b // false 
// 1. 两遍调用ToPrimitive, 返回[object Object] 两遍一致 返回 false
a == b // false
// 两遍不同的引用,返回false
a > b // false
// 同 a < b

a <= b // true
// 按规范其实是处理成 !(a > b) 所以为true
a >= b // true

所以在不相等比较的时候,我们最后还是进行手动的类型转换较为安全


总结

深入了解类型转换的规则,我们就可以很容易取其精华去其糟粕,写出更安全也更简洁可读的代码


本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://fly63.com/article/detial/4243

相关推荐

Js如何将String转化为Int

字符串到整型的一个转换,在面试过程中或者在工作中都会频繁遇到,那么string到js怎么转换呢 ?parseInt方法在format\'00\'开头的数字时会当作2进制转10进制的方法进行转换

Typescript内置类型与自定义类型

大家用过 Typescript 都清楚,很多时候我们需要提前声明一个类型,再将类型赋予变量。例如在业务中,我们需要渲染一个表格,往往需要定义:

Js实现base64,file和blob相互转换

JavaScript实现base64,file和blob相互转换:base64转为Blob;Blob转为base64;base64转换为file;js图片转换为base64;在Java中base64和File相互转换

Js的6种基本数据类型

在JS中一共有六种数据类型 String:字符串 Number:数值 Boolean:布尔值 Null:空值 Undefined:未定义 Object:对象 ,其中String,Number,Boolean,Null,Undefined属于基本数据类型而Object属于引用数据类型

JavaScript判断字符串是否为数字类型

JavaScript中有Number.isInteger可以判断一个字符串是否为整数。不过目前JS没有内置的函数来判断一个数字是否为包含小数的数字:

TypeScript never 类型

在类型理论(数学逻辑中的一种理论)中, 底部类型 是没有值的类型。也称为零或空类型,有时用 falsum(⊥)表示。 数学理论与计算机的发展是相辅相成的,底部类型在计算机科学中也有一定的应用场景。

JS数据类型转换表

下表显示了将不同的JavaScript值转换为Number,String和Boolean的结果:注意:引号(\\\"\\\")中的值表示字符串值。在红色的值是程序员可能不希望被转换为的值。

Js的typeof返回哪些数据类型?

JavaScript中的数据类型:值类型(基本类型):字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol。引用数据类型:对象(Object)、数组(Array)、函数(Function)。

为你的 JavaScript 项目添加智能提示和类型检查

近在做项目代码重构,其中有一个要求是为代码添加智能提示和类型检查。智能提示,英文为 IntelliSense,能为开发者提供代码智能补全、悬浮提示、跳转定义等功能,帮助其正确并且快速完成编码。

JS中的布尔 数字 字符串

JS中所有的值都可以转换成布尔类型 使用Boolean()或者 !!(两个感叹号),JS中所有的值都可以转换成数字类型,使用Number()或+。数字类型转换场景目的只有一个,用于计算,将后台传递的数据,从字符串转换为数字并参与计算

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!