怪异的JavaScript系列(二)

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

为了保证可读性,本文采用意译而非直译。另外,本文版权归原作者所有,翻译仅用于学习。

JavaScript是一门伟大的语言,它拥有非常简洁的语法,庞大的生态系统,以及最重要的:有一个伟大的社区支撑着。同时,我们也知道JavaScript是一个充满技巧性的语言。有些坑足以让我们崩溃,也有些奇淫技巧让我们觉得很有趣。本文的思想源自于Brian Leroux在dotJS2012上的演讲“WTFJS” at dotJS 2012
这里写图片描述

我收集这些例子的主要目的是将它们整理并清楚理解它们的原理。从中学到很多以前不懂的知识是一件很有趣的事情。如果你是初学者,你可以通过学习这些笔记深入理解JavaScript;如果你是一个专业的开发者,那么可以将这些笔记作为一个不错的引用资料。不管怎样,只要读下去,你就会学到新东西的。

## 函数不是函数? > ⚠️ 这是一个低版本的bug,V8(
// Declare a class which extends null
class Foo extends null {}
// -> [Function: Foo]

new Foo instanceof null
// > TypeError: function is not a function
// >     at … … …
备注:经测试高版本(Node.js, v8.1.1)不会出现这个bug。如果你还没升级到高版本,不妨试一下看看? ## 数组相加 如果我们将两个数组相加,结果会怎样?
[1, 2, 3] + [4, 5, 6]  // -> '1,2,34,5,6'
实际上是做了拼接操作,我们来一步一步解释:
[1, 2, 3] + [4, 5, 6]
// 调用 toString()
[1, 2, 3].toString() + [4, 5, 6].toString()
// 字符串拼接
'1,2,3' + '4,5,6'
// ->
'1,2,34,5,6'
## 数组中分号的去除 我们创建一个4个空元素的数组。结果呢,该数组实际上只有3个元素,因为最后一个分号被去掉了。
let a = [,,,]
a.length     // -> 3
a.toString() // -> ',,'
> 末尾分号(Trailing commas)(又叫做final commas)在添加新元素、参数或则属性时候很有用。如果你想增加一个新的属性,并且前一行末尾有使用分号,你可以直接在新的一行添加而不用修改前一行。这可以让版本控制的diff操作更加清晰,代码更少出问题。- [Trailing commas at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Trailing_commas) ## 数组相等匹配非常恐怖 请看:
[] == ''   // -> true
[] == 0    // -> true
[''] == '' // -> true
[0] == 0   // -> true
[0] == ''  // -> false
[''] == 0  // -> true

[null] == ''      // true
[null] == 0       // true
[undefined] == '' // true
[undefined] == 0  // true

[[]] == 0  // true
[[]] == '' // true

[[[[[[]]]]]] == '' // true
[[[[[[]]]]]] == 0  // true

[[[[[[ null ]]]]]] == 0  // true
[[[[[[ null ]]]]]] == '' // true

[[[[[[ undefined ]]]]]] == 0  // true
[[[[[[ undefined ]]]]]] == '' // true
具体请参考[7.2.13 Abstract Equality Comparison](https://www.ecma-international.org/ecma-262/#sec-abstract-equality-comparison) ## undefined和Number 如果不给Number构造函数传入任何参数,那么返回0。如果传入undefined作为参数,会返回NaN。
Number()          // -> 0
Number(undefined) // -> NaN
根据规范: 1. 如果没有参数传入,那么n=0; 2. 否则,n= ToNumber(value); 3. 如果value为undefined,那么ToNumnber(undefined)为NaN。 参考: - [20.1.1 The Number Constructor](https://www.ecma-international.org/ecma-262/#sec-number-constructor) - [7.1.3 ToNumber(argument)](https://www.ecma-international.org/ecma-262/#sec-tonumber) JavaScript坑很多,赶紧使用[fundebug](https://www.fundebug.com)扶一扶! ## parseInt也不是个好东西 parseInt因为它奇怪的行为而出名:
parseInt('f*ck');     // -> NaN
parseInt('f*ck', 16); // -> 15
这是因为parseInt一个字符一个字符去分析,知道遇到无法处理的字符。`f`对应的16进制数为15。 `Infinity`可以转换为对应的数字:
//
parseInt('Infinity', 10) // -> NaN
// ...
parseInt('Infinity', 18) // -> NaN...
parseInt('Infinity', 19) // -> 18
// ...
parseInt('Infinity', 23) // -> 18...
parseInt('Infinity', 24) // -> 151176378
// ...
parseInt('Infinity', 29) // -> 385849803
parseInt('Infinity', 30) // -> 13693557269
// ...
parseInt('Infinity', 34) // -> 28872273981
parseInt('Infinity', 35) // -> 1201203301724
parseInt('Infinity', 36) // -> 1461559270678...
parseInt('Infinity', 37) // -> NaN
小心参数为null的情况:
parseInt(null, 24) // -> 23
> 首先,null被翻译为字符串”null”。”n”在24进制中对于23。– 更多请参考[ “parseInt(null, 24) === 23… wait, what?” at StackOverflow](https://stackoverflow.com/questions/6459758/parseintnull-24-23-wait-what)。
parseInt('n', 24) // -> 23
不要忘记了8进制:
parseInt('06'); // 6
parseInt('08'); // 8 if support ECMAScript 5
parseInt('08'); // 0 if not support ECMAScript 5
如果输入的字符串以0开始,那么为8进制或则10进制。到底是哪一个,要看实现。如果是ECMAScript5,则为10进制。但并不是所有浏览器都支持。因此最安全的方法是调用parseInt的时候指定进制。 parseInt总是将输入转换为字符串。
parseInt({ toString: () => 2, valueOf: () => 1 }) // -> 2
Number({ toString: () => 2, valueOf: () => 1 })   // -> 1
## true和false的数学运算
true + true // -> 2
(true + true) * (true + true) - true // -> 3
我们把true转换为Number来看看就明白了:
Number(true) // -> 1
一元加号运算会尝试将参数转换为number。它会将字符串形式的整数转换为float,非字符串的true,false,和null也会被转换。对于不能转换的值,返回NaN。因此,我们有了一个更加简单的转换方法:
+true // -> 1
当你使用加法或则乘法的时候,ToNumber函数会被调用。根据定义: > 如果参数为true,返回1. 如果参数为false,返回+0. 这就是为什么我们布尔类型的值(true,false)可以和数字相加。 参考: - [12.5.6 Unary + Operator](https://www.ecma-international.org/ecma-262/#sec-unary-plus-operator) - [12.8.3 The Addition Operator (+)](https://www.ecma-international.org/ecma-262/#sec-addition-operator-plus) - [7.1.3 ToNumber(argument)](https://www.ecma-international.org/ecma-262/#sec-tonumber) ## JavaScript中可以使用HTML的评论方式 在JavaScript中,使用`
// valid comment
<!-- valid comment too
支持HTML的评论方式的目的是允许那些不支持` ## NaN是Number NaN的类型是’number’:
typeof NaN // -> 'number'
如果想了解typeof和instanceof如何工作,参考: - [12.5.5 The typeof Operator](https://www.ecma-international.org/ecma-262/#sec-typeof-operator) - [12.10.4 Runtime Semantics: InstanceofOperator(O,C)](https://www.ecma-international.org/ecma-262/#sec-instanceofoperator) ## []和null是对象
typeof []   // -> 'object'
typeof null // -> 'object'

// 但是
null instanceof Object // false
根据typeof的定义,对于null,作为一个没有实现`[[Call]]`的对象,返回”object”。 你可以用toString函数来检查对象的具体(Array, Date, Null)类型:
Object.prototype.toString.call([])
// -> '[object Array]'

Object.prototype.toString.call(new Date)
// -> '[object Date]'

Object.prototype.toString.call(null)
// -> '[object Null]'

猜你喜欢

转载自blog.csdn.net/Fundebug/article/details/80074349