米娜桑,哦哈哟~

本章讲解关于 JavaScript 奇妙的 Bug,与其说是Bug,不如说是语言本身隐藏的奥秘。接下来就看看可能会影响到我们编程的那些Bug吧。

typeof null === "object"


官方自带的Bug,typeof 操作符会返回对应操作数类型的字符串 表示,唯独 null,返回object文档解释说:

在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此,null 的类型标签是 0,typeof null 也因此返回 "object"

指针是一个变量,其储存的值是一个地址,一般空指针储存的地址为 0x00;对象的引用也是一个变量,储存的值也是一个地址,比如:0x000001、0x000002。

而JavaScript会根据实际数据储存值得到标签类型,那么 typeof null === 'object'

NaN !== NaN


如果 var a = NaN , 那么 a !== a

这是 NaN 本身的一个特点,也只有NaN,比较之中不等于它自己。

所以判定是否为NaN有三种方式

isNaN(a) //为NaN或者强制转换为数字后是NaN时,则返回true
Number.isNaN(a) //仅当为NaN时为true
a !== a //成立的唯一情况是 a 的值为 NaN

[ ]![ ] , [ ]![ ]


对于前半段 []!==[]

一个变量在内存中都需要一个空间来存储,而内存会根据其类型分配到栈内存(stack)或堆内存(heap)。

最新的 ECMAScript标准 定义了 8 种数据类型:

  • Null:空指针对象
  • Undefined:未定义
  • Boolean:布尔值
  • Number:整数、浮点数、特殊值(InfinityNaN
  • String:字符串
  • Symbol:一种实例是唯一且不可改变的数据类型
  • BigInt:一种用于表示任意精度格式的整数的数据类型
  • Object:对象

    除了前面7种原始数据类型在声明变量时是储存在栈内存,而对象类型则是在栈内存中存储一个引用地址,该地址指向堆内存的值。
let num = 1,
arr1 = [],
arr2 = [],
arr3 = arr2 console.log(arr1 === arr2) //false
console.log(arr2 === arr3) //true



内存分配如图,虽然它们在堆内存中分配在不同位置的储存的值是等价的。但进行 arr1 === arr2 判断时会对比它们的引用地址,故不为真。

arr2 === arr3 其实本质就是 arr2 === arr2,因此,该改变arr3,其实就是改变arr2,这就引申出浅拷贝深拷贝的话题

对于后半段 []==![]

以相等操作符 == 比较两个值是否相等,在比较前将两个被比较的值转换为相同类型,即隐式转换。转换规则如下,详情请查看 相等性判断 文档

根据上述规则,转换过程如下:

[] == ![]
[] == false //优先执行逻辑非操作,返回false
''== 0 //[]使用toString转换;false转换为数字
0 === 0 //''转化为数字,进行全等判定,比较完毕 。

所以,大多数情况下,不建议使用 == 判定,使用 === 结果更容易预测。由于没有进行隐式转换,=== 评估更快(虽然影响微小)

0.1 + 0.2 !== 0.3


这不仅是JavaScript的Bug,也是计算机语言的“通病”。

从最底层的电路来讲,一般电路通过给电子器件施加电压,根据其电压高低状态(高电平、低电平),也就是所谓的电子器件开关,来实现二值数字逻辑,即0和1。而正是这种方便快捷的状态,决定了计算机采用二进制进行运算。

而小数的二进制大多为无限循环的,如果用每个电子器件开关的状态对应记录这些无限循环的二进制数字,这显然是不可能的。

最终根据IEEE 754标准,0舍1入,使用64位固定长度来表示(ECMAScript®语言规范有所提及),所以有如下结果:

(0.1).toString(2)
//"0.0001100110011001100110011001100110011001100110011001101" (0.2).toString(2)
//"0.001100110011001100110011001100110011001100110011001101" (0.3).toString(2)
//"0.010011001100110011001100110011001100110011001100110011"

而0.1、0.2进行二进制加运算得到的结果应为

//0.0100110011001100110011001100110011001100110011001100111
//对应十进制为 0.30000000000000004
//因此 0.1 + 0.2 !== 0.3

a === 1 && a === 2 , a == 1 && a == 2


看似荒谬的比较,为什么会存在 a 能满足上述条件呢?这得分开讨论。

对于前半段 a === 1 && a===2

如果 a 是原始数据类型,那上述的全等判定就不能成立,所以 a 就需要通过函数进行变形,那么 a 是一个返回1或着2的函数对象。

这个时候我们可以利用 getter 将对象属性绑定到查询该属性时将被调用的函数。而其对象正是 window 对象。

不难得出

let i = 1
Object.defineProperty(window, 'a', {
get() {
return i++
}
})
console.log(a === 1 && a === 2) //true
//当访问 window.a 时候则会实行 a 函数

对于后半段 a === 1 && a===2

在提及 []==![] 讨论过,当比较的两者类型不一致的时候,将进行隐式转换。对应的值会执行其内置的 ToPrimitive() 函数操作:

1、进行 valueOf(),如果得到的为原始数据类型(如Date类型会得到对应的数字),则返回对应原始值,否则进行第2步。

2、进行 toString() ,返回对应原始值,如果失败,抛出 TypeError

let a = {
i: 0,
valueOf() {
return this.i += 1
}
}
console.log(a == 1 && a == 2) //true

那些 JavaScript 自带的奇妙 Bug的更多相关文章

  1. Lazarus下面的javascript绑定另外一个版本bug修正

    Lazarus下面的javascript绑定另外一个版本bug修正 从svn 检出的代码有几个问题 1.fpcjs.pas 单元开始有 {$IFDEF FPC} {$MODE delphi} {$EN ...

  2. cnpm 莫名奇妙bug 莫名奇妙的痛

    cnpm 莫名奇妙bug 莫名奇妙的痛 最近想搭建react@v16 和 react-router@v4,搭建过程打算用vue脚手架webpack模板那套配置方法(webpack3). 由于我之前安装 ...

  3. Javascript 运动中Offset的bug——逐行分析代码,让你轻松了解运动的原理

    我们先来看看这个bug 是怎么产生的. <style type="text/css"> #div1 { width: 200px; height: 200px; bac ...

  4. 关于 javascript event flow 的一个bug

    [1]描述了firefox,safari 有一个bug和DOM 3 规范不一致:在event.currentTarget等于event.target的时候(即event flow处于target ph ...

  5. 原来javascript 自带 encodeURI 和 decodeURI文 方法了

    今天百度一下才知道js 自带 encodeURI 和 decodeURI 方法了,之前还找了其他代码来处理(笑哭了.jpg <script type="text/javascript& ...

  6. JavaScript运算符优先级引起的bug

    [下面是昨天发给同事的邮件,为防止泄露商业机密,隐去了项目名和变量名] ==================================================== 昨天发现Nx代码中的一 ...

  7. 解决JavaScript浮点数(小数) 运算出现Bug的方法

    解决JS浮点数(小数) 运算出现Bug的方法例如37.2 * 5.5 = 206.08 就直接用JS算了一个结果为: 204.60000000000002 怎么会这样, 两个只有一位小数的数字相乘, ...

  8. 是什么让javascript变得如此奇妙

    What Makes Javascript Weird...and AWESOME -> First Class Functions -> Event-Driven Evironment ...

  9. Javascript仿贪吃蛇出现Bug的反思

    bug现象:    图一

随机推荐

  1. Visual Studio 2017 安装心得

    既然VS2017已经发布了,就想安装一下试试,先卸载VS2015, 网上有个完全卸载的东东,https://github.com/Microsoft/VisualStudioUninstaller/r ...

  2. 【温故知新】Java web 开发(二)Servlet 和 简单JSP

    系列一介绍了新建一个 web 项目的基本步骤,系列二就准备介绍下基本的 jsp 和  servlet 使用. (关于jsp的编译指令.动作指令.内置对象不在本文讨论范围之内) 1. 首先,在 pom. ...

  3. Git是什么?

    Git是什么? Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目. Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控 ...

  4. [LINQ2Dapper]最完整Dapper To Linq框架(八)---导航属性

    目录 [LINQ2Dapper]最完整Dapper To Linq框架(一)---基础查询 [LINQ2Dapper]最完整Dapper To Linq框架(二)---动态化查询 [LINQ2Dapp ...

  5. StatePattern(状态模式)-----Java/.Net

    在状态模式(State Pattern)中,类的行为是基于它的状态改变的.这种类型的设计模式属于行为型模式. 在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context ...

  6. jmeter 5.1版本更新说明

    版本5.1 摘要 新的和值得注意的 不兼容的变化 Bug修复 改进 非功能性变化 已知问题和解决方法 谢谢 新的和值得注意的 核心改进 JDBC测试已得到改进,能够设置init SQL语句并添加与不支 ...

  7. .NET Core 3.1之深入源码理解HealthCheck(二)

    写在前面 前文讨论了HealthCheck的理论部分,本文将讨论有关HealthCheck的应用内容. 可以监视内存.磁盘和其他物理服务器资源的使用情况来了解是否处于正常状态. 运行状况检查可以测试应 ...

  8. 1090 危险品装箱 (25分)C语言

    集装箱运输货物时,我们必须特别小心,不能把不相容的货物装在一只箱子里.比如氧化剂绝对不能跟易燃液体同箱,否则很容易造成爆炸. 本题给定一张不相容物品的清单,需要你检查每一张集装箱货品清单,判断它们是否 ...

  9. [开源] SEPP——研发协作一站式管理平台

    演示地址 http://www.seqcer.com/ 仅对chrome浏览器做了完全适配,其他chromium核心浏览器或者firefox.safari也能使用,但是不推荐 仓库地址: 前端:htt ...

  10. IDEA 2019.2及以下版本永久激活教程(亲测可用)

    写在前面 由于最近jetbrains公司开始严厉打击盗版激活码,所以导致一大批激活码失效,我身边的小伙伴对于如此苦恼,但是由于考虑到正版费用还是比较高昂的前提下,所以鉴于此,遂将之前整理的jar包激活 ...