专栏首页Fundebug怪异的JavaScript系列(三)

怪异的JavaScript系列(三)

译者按: JavaScript 有很多坑,经常一不小心就要写 bug。

本文采用意译,版权归原作者所有

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扶一扶!

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?)”:

你的程序中 0.2 和 0.3 会在底层用相近的数据表达。double 类型数据中离 0.2 最近的数要比 0.2 大一点点。离 0.3 最近的 double 类型数据又刚好比 0.3 小一点点。所以,结果就是 0.1+0.2 的结果比 0.3 大。

这个问题非常出名,以至于有一个专门的网站0.30000000000000004.com。在所有使用浮点计算的语言中都有这个问题,不止 JavaScript。

神奇的加法操作

999999999999999; // -> 999999999999999
9999999999999999; // -> 10000000000000000

10000000000000000; // -> 10000000000000000
10000000000000000 + 1; // -> 10000000000000000
10000000000000000 + 1.1; // -> 10000000000000002

这个是依据 IEEE 754-2008 标准确定的二进制浮点运算。当数值大到这个程度,它会取整到最近的偶数。参考:

为 Number 自定义

你可以为NumberString添加自定义函数:

Number.prototype.isOne = function() {
    return Number(this) === 1;
};

(1.0).isOne(); // -> true
(1).isOne(); // -> true
(2.0)
    .isOne()(
        // -> false
        7
    )
    .isOne(); // -> false

你可以想操纵其它对象一样去扩展 Number 对象。不过,如果定义的函数不在它本身的定义规范(Specification)中,那么不建议这么做。这里是一个参考列表:

3 个 number 比较

1 < 2 < 3; // -> true
3 > 2 > 1; // -> false

我们来看看具体的执行过程就明白了:

1 < 2 < 3; // 1 < 2 -> true
true < 3; // true -> 1
1 < 3; // -> true

3 > 2 > 1; // 3 > 2 -> true
true > 1; // true -> 1
1 > 1; // -> false

有趣的数学

3  - 1  // -> 2
 3  + 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 方法会在加法操作前被隐式地调用。

正则也可以做加法?

// Patch a toString method
RegExp.prototype.toString =
    function() {
        return this.source;
    } /
    7 /
    -/5/; // -> 2

参考: 21.2.5.10 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

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

往期参考

版权声明

转载时请注明作者 Fundebug以及本文地址: https://blog.fundebug.com/2018/04/16/javascript-werid-series-3/

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 开源前端脚本错误监控及跟踪解决项目BadJS试用

    BadJS 是 一个web 前端脚本错误监控及跟踪项目。此项目为鹅厂 imweb(qq群:179045421) 团队的开源项目。此项目支持单机,集群,docke...

    Fundebug
  • Mocha浏览器测试入门教程

    在玩转Node.js单元测试博客中,我介绍了测试框架Mocha,对后端Node.js代码进行测试。在这篇博客,我将介绍如何使用Mocha在浏览器中测试JavaS...

    Fundebug
  • Mocha浏览器测试入门教程

    在玩转Node.js单元测试博客中,我介绍了测试框架Mocha,对后端Node.js代码进行测试。在这篇博客,我将介绍如何使用Mocha在浏览器中测试JavaS...

    Fundebug
  • ES6--字符串、正则、数值、数组的扩展

    版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。

    奋飛
  • 人生苦短?试试Groovy进行单元测试

    如果您今天正在编程,那么您很可能听说过单元测试或测试驱动的开发过程。我还没有遇到一个既没有听说过又没有听说过单元测试并不重要的程序员。在随意的讨论中,大多数程序...

    FunTester
  • Flink异步之矛-锋利的Async I/O

    在Flink 流处理过程中,经常需要和外部系统进行交互,用维度表补全事实表中的字段。

    大数据真好玩
  • Spring官网阅读(十一)ApplicationContext详细介绍(上)

    从上图中可以发现,ApplicationContext接口继承了很多接口,这些接口我们可以将其分为五类:

    程序员DMZ
  • 这篇 iTerm2 + Oh My Zsh 教程手把手让你成为这条街最靓的仔

    作为一名程序员,开发环境不舒服会很大程度影响开发效率,所以一定要花时间好好整一下开发环境(好了,我知道你是在给摸鱼找借口)。

    桃翁
  • Android如何获取QQ与微信的聊天记录并保存到数据库详解

    提前说明下:(该方法只适用于监控自己拥有的微信或者QQ ,无法监控或者盗取其他人的聊天记录。本文只写了如何获取聊天记录,服务器落地程序并不复杂,不做赘述。写的仓...

    砸漏
  • Flink异步之矛-锋利的Async I/O

    在Flink 流处理过程中,经常需要和外部系统进行交互,用维度表补全事实表中的字段。

    大数据技术与架构

扫码关注云+社区

领取腾讯云代金券