译者按: 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系列(三)相关推荐

  1. 吐槽Javascript系列三:数组的陷阱

    虽然本系列是吐槽,但并不是为了黑Javascript,而是揭露它的一些特性(怪癖),只有更好的了解它,才能更好的使用它.本篇主要介绍数组中常见的隐患点. 龟速的map 在数组中,map是一个功能很强大 ...

  2. 怪异的JavaScript系列(一)

    译者按: JavaScript有很多坑,经常一不小心就要写bug. 原文: What the f*ck JavaScript? 译者: Fundebug 为了保证可读性,本文采用意译而非直译.另外,本 ...

  3. javascript系列之DOM(三)---事件

    javascript系列之DOM(三)---事件 原文:javascript系列之DOM(三)---事件 事件是javascript跳动的心脏,是DOM所有成分结合的万金油.当我们在WEB 上进行某些 ...

  4. 前端工程师和设计师必读文章推荐【系列三十三】

    <Web 前端开发精华文章推荐>自2011年6月20号发布第一期以来,历经三年半,总共发布了30多期.今天这篇是2015年第2期(总第33期),希望你能在这里发现有用的资料. 梦想天空专注 ...

  5. 深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点

    才华横溢的Stoyan Stefanov,在他写的由O'Reilly初版的新书<JavaScript Patterns>(JavaScript模式)中,我想要是为我们的读者贡献其摘要,那会 ...

  6. 深入理解JavaScript系列(32):设计模式之观察者模式

    介绍 观察者模式又叫发布订阅模式(Publish/Subscribe),它定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使得它们 ...

  7. 前端工程师和设计师必读文章推荐【系列三十六】

    <Web 前端开发精华文章推荐>自2011年6月20号发布第一期以来,历经五年半,总共发布了30多期.今天这篇是2017年第2期(总第36期),希望你能在这里发现有用的资料. 梦想天空专注 ...

  8. 深入理解JavaScript系列:《你真懂JavaScript吗?》答案详解

    介绍 昨天发的<大叔手记(19):你真懂JavaScript吗?>里面的5个题目,有很多回答,发现强人还是很多的,很多人都全部答对了. 今天我们来对这5个题目详细分析一下,希望对大家有所帮 ...

  9. 深入理解JavaScript系列:This? Yes,this!

    介绍 在这篇文章里,我们将讨论跟执行上下文直接相关的更多细节.讨论的主题就是this关键字.实践证明,这个主题很难,在不同执行上下文中this的确定经常会发生问题. 许多程序员习惯的认为,在程序语言中 ...

最新文章

  1. TP返回原生SQL语句:fetchSql
  2. getsockname函数与getpeername函数的使用
  3. Error in moviepy setup command: 'extras_require' must be a dictionary whose values are strings or li
  4. Java编程的11个特点
  5. Java实现简单图书管理系统
  6. Python编程案例:中文金额转换并计算
  7. 保存照片和视频到相册显示
  8. 【HTML教程(一)】HTML标签、模板和实例
  9. Python 每日一记217根据词频生成词云图
  10. 关于720vr 全景平台 全景图片制作系统 仿720云需要注意的坑 诚意贴
  11. 616 java实现发红包案例
  12. ratelimiter php,RateLimiter的 SmoothBursty(非warmup预热)及SmoothWarmingUp(预热,冷启动)...
  13. 将自动获取IP改为固定IP
  14. 组态王与网络mysql数据库通过ODBC连接
  15. qcom 8953 usb hub device descriptor read/64 error -71
  16. java8 获取部门树形结构
  17. (28)打鸡儿教你Vue.js
  18. 怎么把网站上的音乐提取出来
  19. Flink核心篇,四大基石、容错机制、广播、反压、序列化、内存管理、资源管理...
  20. 自动驾驶方程式赛车,微软发布机器学习开源框架 | AI一周学术

热门文章

  1. perl中子程序中参数的两种引用(传递)方式:pass by value and pass by Reference(传入引用)
  2. 2021-07-28
  3. 计算机网络常见面试题整理
  4. 嵌入式IDE-MRS入手使用分享
  5. freemarker生成word文档,通过libreoffice完美转为pdf文件排版不乱(包含调用浏览器打印pdf)
  6. 获取对话框当前cfont_MFC设置对话框、字体对话框、颜色对话框(转)
  7. Linux--管理LVM逻辑卷 --原理+命令双结合(LVM的概述与建立,LVM的管理命令,LVM的应用步骤,磁盘配额的详解)
  8. python和java都是用c文件吗_python是用C实现的,Java是用C++实现的,那为什么不直接用C或C++呢?...
  9. 无线网络技术测试试题(四)
  10. H5页面前端开发常见的兼容性问题解决方法