怪异的JavaScript系列(三)
译者按: JavaScript有很多坑,经常一不小心就要写bug。
- 原文: What the f*ck JavaScript?
- 译者: Fundebug
为了保证可读性,本文采用意译而非直译。另外,本文版权归原作者所有,翻译仅用于学习。
JavaScript是一门伟大的语言,它拥有非常简洁的语法,庞大的生态系统,以及最重要的:有一个伟大的社区支撑着。同时,我们也知道JavaScript是一个充满技巧性的语言。有些坑足以让我们崩溃,也有些奇淫技巧让我们觉得很有趣。本文的思想源自于Brian Leroux在dotJS2012上的演讲“WTFJS” at dotJS 2012。
我收集这些例子的主要目的是将它们整理并清楚理解它们的原理。从中学到很多以前不懂的知识是一件很有趣的事情。如果你是初学者,你可以通过学习这些笔记深入理解JavaScript;如果你是一个专业的开发者,那么可以将这些笔记作为一个不错的引用资料。不管怎样,只要读下去,你就会学到新东西的。
## return 下面的函数返回的结果竟然不是对象`{b:10}`:
(function () {return{b : 10}
})() // -> undefined
不过,如果稍微改写一下,就不一样了:
(function () {return {b : 10}
})() // -> { b: 10 }
这主要是因为有一个**自动行尾加分号**的机制在作怪,会自动在很多新行的行尾添加分号。在第一个例子中,实际上是在return后面添加了分号。
(function () {return ;{b : 10}
})() // -> undefined
JavaScript坑很多,赶紧使用[fundebug](https://www.fundebug.com)扶一扶! ## 0.1+0.2=? 一个众所周知的笑话就是0.1加上0.2竟然不等于0.3。
0.1 + 0.2 // -> 0.30000000000000004
(0.1 + 0.2) === 0.3 // -> false
在StackOverflow上有关提到这样的问题“[浮点数加法运算坏了(Is floating point math broken?)](https://stackoverflow.com/questions/588004/is-floating-point-math-broken)”: > 你的程序中0.2和0.3会在底层用相近的数据表达。double类型数据中离0.2最近的数要比0.2大一点点。离0.3最近的double类型数据又刚好比0.3小一点点。所以,结果就是0.1+0.2的结果比0.3大。 这个问题非常出名,以至于有一个专门的网站[0.30000000000000004.com](http://0.30000000000000004.com/)。在所有使用浮点计算的语言中都有这个问题,不止JavaScript。 ## 神奇的加法操作
999999999999999 // -> 999999999999999
9999999999999999 // -> 1000000000000000010000000000000000 // -> 10000000000000000
10000000000000000 + 1 // -> 10000000000000000
10000000000000000 + 1.1 // -> 10000000000000002
这个是依据IEEE 754-2008标准确定的二进制浮点运算。当数值大到这个程度,它会取整到最近的偶数。参考: - [6.1.6 The Number Type](https://www.ecma-international.org/ecma-262/#sec-ecmascript-language-types-number-type) - [IEEE 754 on Wikipedia](https://en.wikipedia.org/wiki/IEEE_754) ## 为Number自定义 你可以为`Number`和`String`添加自定义函数:
Number.prototype.isOne = function () {return Number(this) === 1
}1.0.isOne() // -> true
1..isOne() // -> true
2.0.isOne() // -> false
(7).isOne() // -> false
你可以想操纵其它对象一样去扩展Number对象。不过,如果定义的函数不在它本身的定义规范(Specification)中,那么不建议这么做。这里是一个参考列表: - [20.1 Number Objects](https://www.ecma-international.org/ecma-262/#sec-number-objects) ## 3个number比较
1 < 2 < 3 // -> true
3 > 2 > 1 // -> false
我们来看看具体的执行过程就明白了:
1 < 2 < 3 // 1 < 2 -> true
true < 3 // true -> 1
1 < 3 // -> true3 > 2 > 1 // 3 > 2 -> true
true > 1 // true -> 1
1 > 1 // -> false
## 有趣的数学
3 - 1 // -> 23 + 1 // -> 4
'3' - 1 // -> 2
'3' + 1 // -> '31''' + '' // -> ''
[] + [] // -> ''
{} + [] // -> 0
[] + {} // -> '[object Object]'
{} + {} // -> '[object Object][object Object]''222' - -'111' // -> 333[4] * [4] // -> 16
[] * [] // -> 0
[4, 4] * [4, 4] // NaN
到底是为什么呢? 下面有一个表供快速参考:
Number + Number -> addition
Boolean + Number -> addition
Boolean + Boolean -> addition
Number + String -> concatenation
String + Boolean -> concatenation
String + String -> concatenation
那么其他例子呢?对于`[]`和`{}`,toPrimitive和toString方法会在加法操作前被隐式地调用。 - [12.8.3 The Addition Operator (+)](https://www.ecma-international.org/ecma-262/#sec-addition-operator-plus) - [7.1.1 ToPrimitive(input [,PreferredType])](https://www.ecma-international.org/ecma-262/#sec-toprimitive) - [7.1.12 ToString(argument)](https://www.ecma-international.org/ecma-262/#sec-tostring) ## 正则也可以做加法?
// Patch a toString method
RegExp.prototype.toString = function() {return this.source
}/7/ - /5/ // -> 2
参考: [21.2.5.10 get RegExp.prototype.source](https://www.ecma-international.org/ecma-262/#sec-get-regexp.prototype.source) ## 箭头函数
let f = () => 10
f() // -> 10
好的,但是下面这个呢:
let f = () => {}
f() // -> undefined
你也许期待着返回`{}`,而不是undefined。着主要是因为大括号也是函数定义语法的一部分。如果你真想返回大括号,可以这么写:
let f = () => ({})
f() // -> {}
## Math.max()比Math.min()小
Math.min(1,4,7,2) // -> 1
Math.max(1,4,7,2) // -> 7
Math.min() // -> Infinity
Math.max() // -> -Infinity
Math.min() > Math.max() // -> true
原因: [Why is Math.max() less than Math.min()? by Charlie Harvey](https://charlieharvey.org.uk/page/why_math_max_is_less_than_math_min) ## String不是String的实例
'str' // -> 'str'
typeof 'str' // -> 'string'
'str' instanceof String // -> false
构造函数`String`返回一个字符串:
typeof String('str') // -> 'string'
String('str') // -> 'str'
String('str') == 'str' // -> true
如果我们用new来构建的话:
new String('str') == 'str' // -> true
typeof new String('str') // -> 'object'
竟然变成了一个对象!
new String('str') // -> [String: 'str']
参考: [21.1.1 The String Constructor](https://www.ecma-international.org/ecma-262/#sec-string-constructor) ## 往期参考 - [怪异的JavaScript系列(一)](https://blog.fundebug.com/2018/04/03/javascript-werid-series-1/) - [怪异的JavaScript系列(二)](https://blog.fundebug.com/2018/04/12/javascript-werid-series-2/)
怪异的JavaScript系列(三)相关推荐
- 吐槽Javascript系列三:数组的陷阱
虽然本系列是吐槽,但并不是为了黑Javascript,而是揭露它的一些特性(怪癖),只有更好的了解它,才能更好的使用它.本篇主要介绍数组中常见的隐患点. 龟速的map 在数组中,map是一个功能很强大 ...
- 怪异的JavaScript系列(一)
译者按: JavaScript有很多坑,经常一不小心就要写bug. 原文: What the f*ck JavaScript? 译者: Fundebug 为了保证可读性,本文采用意译而非直译.另外,本 ...
- javascript系列之DOM(三)---事件
javascript系列之DOM(三)---事件 原文:javascript系列之DOM(三)---事件 事件是javascript跳动的心脏,是DOM所有成分结合的万金油.当我们在WEB 上进行某些 ...
- 前端工程师和设计师必读文章推荐【系列三十三】
<Web 前端开发精华文章推荐>自2011年6月20号发布第一期以来,历经三年半,总共发布了30多期.今天这篇是2015年第2期(总第33期),希望你能在这里发现有用的资料. 梦想天空专注 ...
- 深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点
才华横溢的Stoyan Stefanov,在他写的由O'Reilly初版的新书<JavaScript Patterns>(JavaScript模式)中,我想要是为我们的读者贡献其摘要,那会 ...
- 深入理解JavaScript系列(32):设计模式之观察者模式
介绍 观察者模式又叫发布订阅模式(Publish/Subscribe),它定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使得它们 ...
- 前端工程师和设计师必读文章推荐【系列三十六】
<Web 前端开发精华文章推荐>自2011年6月20号发布第一期以来,历经五年半,总共发布了30多期.今天这篇是2017年第2期(总第36期),希望你能在这里发现有用的资料. 梦想天空专注 ...
- 深入理解JavaScript系列:《你真懂JavaScript吗?》答案详解
介绍 昨天发的<大叔手记(19):你真懂JavaScript吗?>里面的5个题目,有很多回答,发现强人还是很多的,很多人都全部答对了. 今天我们来对这5个题目详细分析一下,希望对大家有所帮 ...
- 深入理解JavaScript系列:This? Yes,this!
介绍 在这篇文章里,我们将讨论跟执行上下文直接相关的更多细节.讨论的主题就是this关键字.实践证明,这个主题很难,在不同执行上下文中this的确定经常会发生问题. 许多程序员习惯的认为,在程序语言中 ...
最新文章
- TP返回原生SQL语句:fetchSql
- getsockname函数与getpeername函数的使用
- Error in moviepy setup command: 'extras_require' must be a dictionary whose values are strings or li
- Java编程的11个特点
- Java实现简单图书管理系统
- Python编程案例:中文金额转换并计算
- 保存照片和视频到相册显示
- 【HTML教程(一)】HTML标签、模板和实例
- Python 每日一记217根据词频生成词云图
- 关于720vr 全景平台 全景图片制作系统 仿720云需要注意的坑 诚意贴
- 616 java实现发红包案例
- ratelimiter php,RateLimiter的 SmoothBursty(非warmup预热)及SmoothWarmingUp(预热,冷启动)...
- 将自动获取IP改为固定IP
- 组态王与网络mysql数据库通过ODBC连接
- qcom 8953 usb hub device descriptor read/64 error -71
- java8 获取部门树形结构
- (28)打鸡儿教你Vue.js
- 怎么把网站上的音乐提取出来
- Flink核心篇,四大基石、容错机制、广播、反压、序列化、内存管理、资源管理...
- 自动驾驶方程式赛车,微软发布机器学习开源框架 | AI一周学术
热门文章
- perl中子程序中参数的两种引用(传递)方式:pass by value and pass by Reference(传入引用)
- 2021-07-28
- 计算机网络常见面试题整理
- 嵌入式IDE-MRS入手使用分享
- freemarker生成word文档,通过libreoffice完美转为pdf文件排版不乱(包含调用浏览器打印pdf)
- 获取对话框当前cfont_MFC设置对话框、字体对话框、颜色对话框(转)
- Linux--管理LVM逻辑卷 --原理+命令双结合(LVM的概述与建立,LVM的管理命令,LVM的应用步骤,磁盘配额的详解)
- python和java都是用c文件吗_python是用C实现的,Java是用C++实现的,那为什么不直接用C或C++呢?...
- 无线网络技术测试试题(四)
- H5页面前端开发常见的兼容性问题解决方法