【翻译】What the f*ck JavaScript?(JavaScript你怎么这样啊???)

来源:互联网 发布:阿里云怎么代理加盟 编辑:程序博客网 时间:2024/05/22 05:06

翻译自 What the f*ck JavaScript
文中提到了挺有趣的一些JavaScript现象,对于深入了解和学习这么语言很有用。
粗的斜体字是翻译的人(也就是我XD)自己加的一些注解之类的
如果有啥翻译的不对或者不好的地方一定要告诉我呀,谢谢大家>3<

What the f*ck JavaScript?

JavaScript你怎么这样啊

A list of funny and tricky JavaScript examples
一篇有很多JavaScript有趣例子的文章

JavaScript is a great language. It has a simple syntax, large ecosystem and, what is most important, a great community.

JavaScript是一门很棒的语言。它有非常简单的语法,很有规模的生态,最重要的是还有一个非常棒的社区。

At the same time, we all know that JavaScript is quite a funny language with tricky parts. Some of them can quickly turn our everyday job into hell, some of them can make us laugh out loud.

同时,我们也知道,JavaScript是一门很有趣的语言,它有很多非常微妙的部分。有一些可以刚让我们每天的工作秒变地狱,还有一些可以让我们开怀大笑。

The original idea for WTFJS belongs to Brian Leroux. This list is highly inspired by his talk “WTFJS” at dotJS 2012:

这篇文章最最原始的想法是源自Brain Leroux。这篇文章的灵感很大程度上都源自于他2012年在dotJS的演讲“WTFJS“:

Node Packaged Manuscript

node封装的文件包

You can install this handbook using npm. Just run:
你可以通过npm安装这本指南。运行:

$ npm install -g wtfjs

You should be able to run wtfjs at the command line now. This will open the manual in your selected $PAGER. Otherwise, you may continue reading on here.

你现在应该可以在编译器上运行wtfjs了。他会在你选定的$PAGER上打开。或者你也可以继续在这里阅读它。

The source is available here: https://github.com/denysdovhan/wtfjs

源文件在这里可以找到https://github.com/denysdovhan/wtfjs

Table of contents

目录

  • Motivation
  • Notation
  • Examples
    1. [] is equal ![]
    2. true is false
    3. baNaNa
    4. NaN is not a NaN
    5. It’s a fail
    6. [] is truthy, but not true
    7. null is falsy, but not false
    8. Minimal value is greater than zero
    9. function is not function
    10. Adding arrays
    11. Trailing commas in array
    12. Array equality is a monster
    13. undefined and Number
    14. parseInt is a bad guy
    15. Math with true and false
    16. HTML comments are valid in JavaScript
    17. NaN is not a number
    18. [] and null are objects
    19. Magically increasing numbers
    20. Precision of 0.1 + 0.2
    21. Patching numbers
    22. Comparison of three numbers
    23. Funny math
    24. Addition of RegExps
    25. Strings aren’t instances of String
    26. Calling functions with backticks
    27. Call call call
    28. A constructor property
    29. Object as a key of object’s property
    30. Accessing prototypes with proto
    31. ${{Object}}
    32. Destructuring with default values
    33. Dots and spreading
    34. Labels
    35. Nested labels
    36. Insidious try..catch
    37. Is this multiple inheritance?
    38. A generator which yields itself
    39. A class of class
    40. Non-coercible objects
    41. Tricky arrow functions
    42. Tricky return
    43. Accessing object properties with arrays
  • Other resources
  • License

Motivation

动机

Just for fun
— “Just for Fun: The Story of an Accidental Revolutionary”, Linus Torvalds
只为欢乐
—《只为欢乐:一次偶然的革命》, Linus Torvalds
中文译名:《只为欢乐》(Just for Fun: The Story of an Accidental Revolutionary),是Linux核心的创建者林纳斯·托瓦兹(Linus Torvalds)的幽默自传。

The primary goal of this list is to collect some crazy examples and explain how they work, if possible. Just because it’s fun to learn something that we didn’t know before.
这篇文章的主要目的是收集一些疯狂的例子并且尽可能解释清楚他们是怎么运作的。只是因为他们学起来很有趣,并且我们之前从未了解过。

If you are a beginner, you can use these notes to get a deeper dive into JavaScript. I hope these notes will motivate you to spend more time reading the specification.
如果你是初学者,你可以借助这篇文章来更深入了解JavaScript。我希望这篇文章能够成为你想更深入理解JavaScript规范的动力。

If you are a professional developer, you can consider these examples as a great reference for all of the quirks and unexpected edges of our beloved JavaScript.
如果你是一个专业开发人员,你可以将这些例子作为我们亲爱的JavaScript的一些出人意料行为的一些参考。

In any case, just read this. You’re probably going to find something new.
无论如何,来看看吧。你也许会发现一些新鲜的东西。

Notation

符号

// -> is used to show the result of an expression. For example:
// ->用来表示表达式的结果。例如:

1 + 1 // -> 2

// > means the result of console.log or another output. For example:
// >表示console.log的结果或其他输出。例如:

console.log(‘hello, world!’) // > hello, world!

// is just a comment used for explanations. Example:
//只是用来解释的注释。例如:

// Assigning a function to foo constantconst foo = function () {}

Examples

例子

1. []is equal ![]

1. []![]相等

Array is equal not array:
数组与非数组相等:

[] == ![] // -> true

Explanation:
说明:

  • 12.5.9 Logical NOT Operator(!)
  • 7.2.13 Abstract Equality Comparison

2. true is false

2. true是false

!!'false' ==  !!'true'  // -> true!!'false' === !!'true' // -> true

Explanation:
说明:
Consider this step-by-step:
一步一步地考虑:

true == 'true'    // -> truefalse == 'false'  // -> false// 'false' is not empty string, so it's truthy value//因为字符串'false'并不是一个空字符串,所以它的值为真!!'false' // -> true!!'true'  // -> true
  • 7.2.13 Abstract Equality Comparison

3. baNaNa

3.小黄人(ba~ba~banana)

'b' + 'a' + + 'a' + 'a'

This is an old-school joke in JavaScript, but remastered. Here’s the original one:
这是一个JavaScript的老笑话,但是从不过时。它的原型是:

'foo' + + 'bar' // -> 'fooNaN'

Explanation:
说明:

The expression is evaluated as 'foo' + (+'bar'), which converts ‘bar’ to not a number.
表达式通过'foo' + (+'bar')求值,这里把'bar'转换为了一个非数值(NaN)。

  • 12.8.3 The Addition Operator (+)

4. NaN is not a NaN

4. NaN不等于它自己

NaN === NaN // -> false

Explanation:
说明:

The specification strictly defines the logic behind this behavior:
规范严格定义了行为背后的逻辑:

If Type(x) is different from Type(y), return false.
If Type(x) is Number, then
If x is NaN, return false.
If y is NaN, return false.
… … …
— 7.2.14 Strict Equality Comparison

Following the definition of NaN from the IEEE:
按照IEEE对NaN的定义:

Four mutually exclusive relations are possible: less than, equal, greater than, and unordered. The last case arises when at least one operand is NaN. Every NaN shall compare unordered with everything, including itself.
有四个互斥关系是可能的:小于、等于、大于以及无穷。最后一个情况出现在至少一个操作数是NaN的情况下。每个NaN都可以比较无穷尽的一切,包括它自己。(NaN不等于任何一个数值,包括它自己)
— “What is the rationale for all comparisons returning false for IEEE754 NaN values?” at StackOverflow

5. It’s a fail

5. 这是一个fail

You would not believe, but …
你可能不会相信,但是…

(![]+[])[+[]]+(![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]// -> 'fail'

Explanation:
说明:
By breaking that mass of symbols into pieces, we notice that the following pattern occurs often:
把这一堆符号分解成片段来看,我们注意到了以下这几种模式:

(![]+[]) // -> 'false'![]      // -> false

So we try adding[]to false. But due to a number of internal function calls (binary + Operator -> ToPrimitive -> [[DefaultValue]]) we end up converting the right operand to a string:
所以我们尝试把[]false加在一起。但是由于一些函数内部的调用(二级制+运算符 -> 转换为原始值 -> [[默认值]]) 我们最后把右边的操作数转换为了一个字符串:

(![]+[].toString()) // 'false'

Thinking of a string as an array we can access its first character via [0]:
把这个字符串想成一个数组,我们可以访问它的第一个字符,用[0]

'false'[0] // -> 'f'

The rest is obvious, but the i is tricky. The i in fail is grabbed by generating the string 'falseundefined' and grabbing the element on index ['10']
剩下的就很明显了,但是得到字母i需要一点技巧。fail中的i是通过生成字符串'falseundefined'以及取index['10']得到的

6. [] is truthy, but not true

6. []为真,但不等于true

An array is a truthy value, however, it’s not equal to true.
数组为真值,但它并不等于true。

!![] // -> true
[] == true // -> false

Explanation:
说明:
Here are links to the corresponding sections in the ECMA-262 specification:
以下是ECMA-262规范中相应部分的链接:

  • 12.5.9 Logical NOT Operator (!)
  • 7.2.13 Abstract Equality Comparison

7. null is falsy, but not false

7. null 为假, 但不等于 false

Despite the fact that null is a falsy value, it’s not equal to false.
尽管null 值为假,但是并不等于false.

!!null        // -> falsenull == false // -> false

At the same time, other falsy values, like 0 or '' are equal to false.
与此同时,还有一些其他为假的值,如 0'',他们可以与false划等号。

0 == false  // -> true'' == false // -> true

Explanation:
说明:

The explanation is the same as for previous example. Here’s the corresponding link:
说明与上一个例子相等,这是有关的链接:

  • 7.2.13 Abstract Equality Comparison

8. Minimal value is greater than zero

8. 最小值大于0

Number.MIN_VALUE is the smallest number, which is greater than zero:
Number.MIN_VALUE是最小的值,它比0要大:

Number.MIN_VALUE > 0 // -> true

Explanation:
说明:

Number.MIN_VALUE is 5e-324, i.e. the smallest positive number that can be represented within float precision, i.e. that’s as close as you can get to zero. It defines the best resolution that floats can give you.
Number.MIN_VALUE5e-324,也就是说,最小的正数可以被浮点精度表示,也就是说这是最接近0的数。他定义了能达到的最大的浮点精度。

Now the overall smallest value is Number.NEGATIVE_INFINITY although it’s not really numeric in a strict sense.
现在,整体最小的值是Number.NEGATIVE_INFINITY,尽管它在严格意义上并不是真正的数字。

— “Why is 0 less than Number.MIN_VALUE in JavaScript?” at StackOverflow

  • 20.1.2.9 Number.MIN_VALUE

9. function is not function

9. function不是function

⚠️ A bug present in V8 v5.5 or lower (Node.js <=7) ⚠️⚠️ 小于等于v5.5版本的v8的一个bug(Node.js <=7) ⚠️

All of you know about the annoying undefined is not a function, but what about this?
你们都知道烦人的undefined并不是一个函数,但是这个呢?

// Declare a class which extends null//声明一个扩展自null的类class Foo extends null {}// -> [Function: Foo]new Foo instanceof null// > TypeError: function is not a function// >     at … … …

Explanation:
说明:
This is not a part of the specification. It’s just a bug that has now been fixed, so there shouldn’t be a problem with it in the future.
这不是规范的一部分。 这只是一个已经修复的错误,所以今后不应该有问题了。

10. Adding arrays

10. 相加的数组

What if you try to add two arrays?
如果你尝试将两个数组相加会发生什么?

[1, 2, 3] + [4, 5, 6]  // -> '1,2,34,5,6'

Explanation:
说明:

The concatenation happens. Step-by-step, it looks like this:
这样连接的情况真的会发生。分解来看,它们是这样的:

[1, 2, 3] + [4, 5, 6]// call toString()[1, 2, 3].toString() + [4, 5, 6].toString()// concatenation'1,2,3' + '4,5,6'// ->'1,2,34,5,6'

11. Trailing commas in array

11. 数组末尾的逗号

You’ve created an array with 4 empty elements. Despite all, you’ll get an arrary with three elements, because of trailing comas:
你创建了一个有四个空元素的数组。尽管如此,你会得到一个拥有三个元素的数组,因为末尾的逗号(会被忽略):

let a = [,,,]a.length     // -> 3a.toString() // -> ',,'

Explanation:
说明:

Trailing commas (sometimes called “final commas”) can be useful when adding new elements, parameters, or properties to JavaScript code. If you want to add a new property, you can simply add a new line without modifying the previously last line if that line already uses a trailing comma. This makes version-control diffs cleaner and editing code might be less troublesome.
末尾的逗号(有时候被称作最后的逗号)在JavaScript代码中添加新的元素,参数或属性的时候非常有用。如果你想添加一个新属性,您可以简单地添加一个新行,而不修改之前的最后一行,如果该行已经使用了一个逗号。这使得版本控制差异更清晰,编辑代码可能不那么麻烦。

— Trailing commas at MDN

12. Array equality is a monster

12. 数组相等犹如小怪兽

Array equality is a monster in JS, think below:
数组相等在JS中是怪物一样的存在,思考如下代码:

[] == ''   // -> 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

Explanation:
说明:

You should be very careful for above! This is a complex examples, but it’s described in 7.2.13 Abstract Equality Comparison section of the specification.
你应该非常小心上边的这些表达式! 这是一个复杂的例子,但在规范的7.2.13抽象平等比较部分中有详细的描述。

13. undefined and number

13. undefinednumber

If we don’t pass any arguments into the Number constructor, we’ll get 0. The value undefined is assigned to formal arguments when there are no actual arguments, so you might expect that Number without arguments takes undefined as a value of its parameter. However, when we pass undefined, we will get NaN.
如果我们不传给Number构造器任何实参,我们会得到0。当没有实参时,undefined将会被分配给参数,所以你也许会认为没有实参的Number会将undefined作为参数传入。但是当我们传入undefined时,得到的是NaN

Number()          // -> 0Number(undefined) // -> NaN

Explanation:
说明:

According to the specification:
根据规范,有:

  1. If no arguments were passed to this function’s invocation, let n be +0.
    如果没有参数传给函数调用,那么n为+0
  2. Else, let n be ? ToNumber(value).
    另外,让n是啥?ToNumber(value)
  3. In case of undefined, ToNumber(undefined) should return NaN.
    undefined的情况下,ToNumber(undefined)应该返回NaN

Here’s the corresponding section:
这是相关的部分:

  • 20.1.1 The Number Constructor
  • 7.1.3 ToNumber(argument)

14. parseInt is a bad guy

14. parseInt是个坏蛋

parseInt is famous by its quirks:
parseInt以他的怪异闻名:

parseInt('f*ck');     // -> NaNparseInt('f*ck', 16); // -> 15

Explanation: This happens because parseInt will continue parsing character-by-character until it hits a character it doesn’t know. The f in ‘f*ck’ is the hexadecimal digit 15.
说明:这是因为parseInt逐行解析字符,直到它触及不知道的字符。 ‘f * ck’中的f是十六进制数字15。

Parsing Infinity to integer is something…
解析Infinity(无穷大)到整数会这样…

//parseInt('Infinity', 10) // -> NaN// ...parseInt('Infinity', 18) // -> NaN...parseInt('Infinity', 19) // -> 18// ...parseInt('Infinity', 23) // -> 18...parseInt('Infinity', 24) // -> 151176378// ...parseInt('Infinity', 29) // -> 385849803parseInt('Infinity', 30) // -> 13693557269// ...parseInt('Infinity', 34) // -> 28872273981parseInt('Infinity', 35) // -> 1201203301724parseInt('Infinity', 36) // -> 1461559270678...parseInt('Infinity', 37) // -> NaN

Be careful with parsing null too:
null也要小心:

parseInt(null, 24) // -> 23

Explanation:
说明:

It’s converting null to the string "null" and trying to convert it. For radixes 0 through 23, there are no numerals it can convert, so it returns NaN. At 24, “n”, the 14th letter, is added to the numeral system. At 31, “u”, the 21st letter, is added and the entire string can be decoded. At 37 on there is no longer any valid numeral set that can be generated and NaN is returned.
这个行为将null转换为了字符串"null",并尝试转换它。对于基数0到23,没有可以转换的数字,因此返回NaN。 在24处,将第14个字母的“n”加到数字系统中。在31处,添加第二十一个字母“u”,整个字符串就被解码了。 在37处,不再有可以生成的有效数字集合,并且返回NaN。

— “parseInt(null, 24) === 23… wait, what?” at StackOverflow

Don’t forget about octals:
不要忘记了八进制数:

parseInt(‘06’); // 6
parseInt(‘08’); // 8 if support ECMAScript 5
parseInt(‘08’); // 0 if not support ECMAScript 5

Explanation: If the input string begins with “0”, radix is eight (octal) or 10 (decimal). Exactly which radix is chosen is implementation-dependent. ECMAScript 5 specifies that 10 (decimal) is used, but not all browsers support this yet. For this reason always specify a radix when using parseInt.
说明:如果输入字符串以”0”开头,那么基数为八进制或十进制。选择哪个基数是依赖于实现的。ECMAScript 5指定使用十进制,但并不是所有浏览器都支持。 因此,在使用parseInt时总是指定一个基数。

parseInt always convert input to string:
parseInt会把input转换为字符串:

parseInt({ toString: () => 2, valueOf: () => 1 }) // -> 2Number({ toString: () => 2, valueOf: () => 1 })   // -> 1

15. Math with true and false

15. Math与truefalse

Let’s do some math:
我们来整点数学问题:

true + true // -> 2(true + true) * (true + true) - true // -> 3

Hmmm…
这是咋回事呢…

Explanation:
说明:

We can coerce values to numbers with the Number constructor. It’s quite obvious that true will be coerced to 1:
我们可以使用构造函数Number将值强制转换为数字。很明显的,true会被强制转换为1

Number(true) // -> 1

The unary plus operator attempts to convert its value into a number. It can convert string representations of integers and floats, as well as the non-string values true, false, and null. If it cannot parse a particular value, it will evaluate to NaN. That means we can coerce true to 1 easier:
一元加运算符会尝试将值转换成数字。它可以将字符串转换整数和浮点数的形式,将非字符串转换为truefalsenull。 如果不能解析一个特定的值,它会被转换为NaN。 这意味着我们可以非常容易地将true转换为1

+true // -> 1

When you’re performing addition or multiplication, the ToNumber method is invoked. According to the specification, this method returns:
每当你使用加法或乘法时,ToNumber方法就会被调用,由于这个特性,方法将返回:

If argument is true, return 1. If argument is false, return +0.
如果参数为真,返回1。如果参数为假,返回+0。

That’s why we can add boolean values as regular numbers and get correct results.
这就是为什么我们可以将布尔值与常规数字相加还可以获得正确的结果。

Corresponding sections:
相关部分:

  • 12.5.6 Unary + Operator
  • 12.8.3 The Addition Operator (+)
  • 7.1.3 ToNumber(argument)

16. HTML comments are valid in JavaScript

16. HTML的注释在JavaScript中也可用

You will be impressed, but <!--(which is known as HTML comment) is a valid comment in JavaScript.
也许你会感到很惊讶,但是<!--(一般我们都作为html的注释用)作为注释,在JavaScript中也是可用的。

// valid comment<!-- valid comment too

Explanation:
说明:

Impressed? HTML-like comments were intended to allow browsers that didn’t understand the <script> tag to degrade gracefully. These browsers, e.g. Netscape 1.x are no longer popular. So there is really no point in putting HTML comments in your script tags anymore.
你也感到很惊讶吧?类HTML的注释可以让读不懂<script>标签的浏览器优雅降级。能够做到这样的浏览器,如Netscape 1.x他们不再受到大众欢迎。所以也没有必要将HTML的注释用到script标签中。

Since Node.js is based on the V8 engine, HTML-like comments are supported by the Node.js runtime too. Moreover, they’re a part of the specification:
由于Node.js基于V8引擎,Node.js运行时也支持类HTML的注释。 并且它们是规范的一部分:

  • B.1.3 HTML-like Comments

这个特性最近正好在《DOM编程的艺术》中读到了,但需要注意的一点是:如果在HTML文档中需要用-->来结束注释,但是JavaScript中不能这样做,因为它会把-->视为注释内容的一部分。
另外,在HTML中这样的注释允许跨越多个行,但JavaScript脚本中要求这种注释每行都必须在前头加上<!--作为标志。

17. NaN is not(x) a number

17. NaN 不算是数字(才怪)

Type of NaN is a 'number':
NaN的类型是'number'

typeof NaN            // -> 'number'

Explanation:
说明:

Explanations of how typeof and instanceof operators work:
了解typeofinstanceof是怎么工作的:

  • 12.5.5 The typeof Operator
  • 12.10.4 Runtime Semantics: InstanceofOperator(O,C)

18. [] and null are objects

18. []null也是对象

typeof []   // -> 'object'typeof null // -> 'object'// howevernull instanceof Object // false

Explanation:
说明:

The behavior of typeof operator is defined in this section of the specification:
typeof的行为在规范中有定义:

  • 12.5.5 The typeof Operator

According to the specification, the typeof operator returns a string according to Table 35: typeof Operator Results. For null, ordinary, standard exotic and non-standard exotic objects, which do not implement [[Call]], it returns the string "object".
根据规范与 Table 35: typeof Operator Results, typeof返回一个字符串。对于null这个不执行[[Call]]的既普通又标准既异常又非标准的对象,它返回的是字符串"object"

However, you can check the type of an object by using the toString method.
总之,你可以用toString方法来确认一个对象的类型。

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

19. Magically increasing numbers

19. 会魔法般增长的数字

999999999999999  // -> 9999999999999999999999999999999 // -> 1000000000000000010000000000000000       // -> 1000000000000000010000000000000000 + 1   // -> 1000000000000000010000000000000000 + 1.1 // -> 10000000000000002

Explanation:
说明:

This is caused by IEEE 754-2008 standard for Binary Floating-Point Arithmetic. At this scale, it rounds to the nearest even number. Read more:
这是由IEEE 754-2008标准的二进制浮点运算标准引起的。在这个大小时,它会舍入到最接近的偶数。了解更多:

  • 6.1.6 The Number Type
  • IEEE 754 on Wikipedia

20. Precision of 0.1 + 0.2

20. 0.1 + 0.2 的精度

A well-known joke. An addition of 0.1 and 0.2 is deadly precise:
这是一个非著名笑话。0.1与0.2相加的结果准确度惊人:

0.1 + 0.2 // -> 0.30000000000000004(0.1 + 0.2) === 0.3 // -> false

Explanation:
说明:

The answer for the ”Is floating point math broken?” question on StackOverflow:
在StackOverflow上关于”浮点数计算是不是瓦特了?“

The constants 0.2 and 0.3 in your program will also be approximations to their true values. It happens that the closest double to 0.2 is larger than the rational number 0.2 but that the closest double to 0.3 is smaller than the rational number 0.3. The sum of 0.1 and 0.2 winds up being larger than the rational number 0.3 and hence disagreeing with the constant in your code.
程序中的常量0.20.3 实际上是无限近似于他们的真值的。会有这样一种现象:最接近0.2的 双精度浮点数比有理数0.2要大,但是最接近0.3的 双精度浮点数比有理数0.3要小。结果导致0.10.2的和要比有理数0.3要大一点,因此它的结果并不等于你代码中的常数。

This problem is so known that there is even a website called 0.30000000000000004.com. It occurs in every language that uses floating-point math, not just JavaScript.
这个问题太有名了,现在甚至有一个网站叫做0.30000000000000004.com。不仅仅是JavaScript,所有用到了浮点数的程序语言中都会有这个问题。

21. Patching numbers

21. Number补完计划

You can add your own methods to wrapper objects like Number or String.
你可以将自己的方法添加到像NumberString包装对象中

Number.prototype.isOne = function () {  return Number(this) === 1}1.0.isOne() // -> true1..isOne()  // -> true2.0.isOne() // -> false(7).isOne() // -> false

Explanation:
说明:

Obviously, you can extend Number object like any other object in JavaScript. However, it’s not recommended if the behavior of defined method is not a part of the specification. Here is the list of Number’s properties:
显然,在JavaScript中你可以像扩展其他对象一样扩展Number对象。尽管如此,如果添加的方法行为没有存在于规范中的话,这种行为并不被推荐。以下是Number的属性列表:

  • 20.1 Number Objects

22. Comparison of three numbers

22. 比较三个数字

1 < 2 < 3 // -> true3 > 2 > 1 // -> false

Explanation:
说明:

Why does this work that way? Well, the problem is in the first part of an expression. Here’s how it works:
为什么会发生这种情况呢?这个问题发生在表达式的开头。它是这样运作的:

1 < 2 < 3 // 1 < 2 -> truetrue  < 3 // true -> 11     < 3 // -> true3 > 2 > 1 // 3 > 2 -> truetrue  > 1 // true -> 11     > 1 // -> false

We can fix this with Greater than or equal operator (>=):
通过设置为大于等于(>=)我们可以解决这个问题:

3 > 2 >= 1 // true

Read more about Relational operators in the specification:
详细了解关系运算符的规范:

  • 12.10 Relational Operators

23. Funny math

23. 搞笑的math

Often the results of arithmetic operations in JavaScript might be quite unexpected. Consider these examples:
通常JavaScript中的算术运算结果可能是非常意想不到的。 思考以下例子:

 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

Explanation:
说明:

What’s happening in the first four examples? Here’s a small table to understand addition in JavaScript:
前四个例子中发生了什么?来看看这个列表吧,能帮助我们理解JavaScript中的加法运算符:

Number  + Number  -> addition(相加)Boolean + Number  -> additionBoolean + Boolean -> additionNumber  + String  -> concatenation(连接)String  + Boolean -> concatenationString  + String  -> concatenation

What about other examples? A ToPrimitive and ToString methods are being implicitly called for [] and {} before addition. Read more about evaluation process in the specification:
另外一些例子怎么解释?当[]{}在相加前被调用时,隐式调用了ToPrimitiveToString 方法。了解更多规范中的分析过程:

  • 12.8.3 The Addition Operator (+)
  • 7.1.1 ToPrimitive(input [,PreferredType])
  • 7.1.12 ToString(argument)

24. Addition of RegExps

24. 正则表达式的补充

Did you know you can add numbers like this?
你知道可以这样把数字加起来吗?

// Patch a toString method//添加一个toString方法RegExp.prototype.toString = function() {  return this.source}/7/ - /5/ // -> 2

Explanation:
说明:

  • 21.2.5.10 get RegExp.prototype.source

25. Strings aren’t instances of String

25. 字符串不是 String的实例

'str' // -> 'str'typeof 'str' // -> 'string''str' instanceof String // -> false

Explanation:
说明:

The String construnctor returns a string:
String构造函数返回一个字符串:

typeof String('str')   // -> 'string'String('str')          // -> 'str'String('str') == 'str' // -> true

Let’s try with a new:
我们换一个试试:

new String('str') == 'str' // -> truetypeof new String('str')   // -> 'object'

Object? What’s that?
对象?啥玩意?

new String('str') // -> [String: 'str']

More information about the String constructor in the specification:
获取规范中更多有关String构造函数的信息:

  • 21.1.1 The String Constructor

26. Calling functions with backticks

26. 用反引号调用函数

Let’s declare a function which logs all params into the console:
我们来声明一个将所有参数记录到控制台中的函数:

function f(...args) {  return args}

No doubt, you know you can call this function like this:
毫无疑问地,你知道可以这样调用这个函数:

f(1, 2, 3) // -> [ 1, 2, 3 ]

But did you know you can call any function with backticks?
但你知道你可以用反引号调用任何函数吗?

f`true is ${true}, false is ${false}, array is ${[1,2,3]}`// -> [ [ 'true is ', ', false is ', ', array is ', '' ],// ->   true,// ->   false,// ->   [ 1, 2, 3 ] ]

Explanation:
说明:

Well, this is not magic at all if you’re familiar with Tagged template literals. In the example above, f function is a tag for template literal. Tags before template literal allow you to parse template literals with a function. The first argument of a tag function contains an array of string values. The remaining arguments are related to the expressions. Example:
其实,如果你熟悉Tagged模板文字,就会知道这根本就不是魔术。在上边的例子中,f函数是一个模板文字的标签。模板文字之前的标签允许您使用函数解析模板文字。标签函数的第一个参数包含字符串值的数组。 其余的参数与表达式有关。 例:

function template(strings, ...keys) {  // do something with strings and keys…}

This is the magic behind famous library called styled-components, which is popular in the React community.
这是magic behind ,它来自于React社区里非常流行的一个叫做styled-components的程序库。

Link to the specification:
规范的链接:

  • 12.3.7 Tagged Templates

27. Call call call

27. 连环call

Found by @cramforce

console.log.call.call.call.call.call.apply(a => a, [1, 2])

Explanation:
说明:

Attention, it could break your mind! Try to reproduce this code in your head: we’re applying the call method using the apply method. Read more:
注意,可能会伤了你的心! 尝试在您的头脑中重现此代码:我们在apply方法中应用“call”方法。 了解更多:

  • 19.2.3.3 Function.prototype.call(thisArg, …args)
  • 19.2.3.1 Function.prototype.apply(thisArg, argArray)

28. A constructor property

28. 一个构造函数属性

const c = 'constructor'c[c][c]('console.log("WTF?")')() // > WTF?

Explanation:
说明:

Let’s consider this example step-by-step:
我们来一步一步地剖析这个例子:

// Declare a new constant which is a string// 声明一个字符串常量 'constructor'const c = 'constructor'// c is a string// c是一个字符串c // -> 'constructor'// Getting a constructor of string// 获取一个字符串的构造函数c[c] // -> [Function: String]// Getting a constructor of constructor// 获取一个构造函数的构造函数c[c][c] // -> [Function: Function]// Call the Function constructor and pass// the body of new function as an argument// 调用Function构造函数并传递新函数的主体作为参数c[c][c]('console.log("WTF?")') // -> [Function: anonymous]// And then call this anonymous function// The result is console-logging a string 'WTF?'// 然后调用这个匿名函数// 结果是在控制台打印出一个字符串'WTF?'c[c][c]('console.log("WTF?")')() // > WTF?

An Object.prototype.constructor returns a reference to the Object constructor function that created the instance object. In case with strings it is String, in case with numbers it is Number and so on.
一个Object.prototype.constructor返回一个引用对象的构造函数创建的实例对象。在字符串的情况下,它是String,在数字的情况下它是Number等等。

  • Object.prototype.constructor at MDN
  • 19.1.3.1 Object.prototype.constructor

29. Object as a key of object’s property

29. 对象可以作为对象属性的键值

{ [{}]: {} } // -> { '[object Object]': {} }

Explanation:
说明:

Why does this work so? Here we’re using a Computed property name. When you pass an object between those brackets, it coerces object to a string, so we get the property key '[object Object]' and the value {}.
为什么会变成这样婶儿呢?这里我们使用一个计算属性名。当您在这些方括号之间传递对象时,会将对象强制转换为字符串,因此我们可以获取属性键“[object Object]”和值“{}”。

We can make “brackets hell” like this:
我们可以让括号这样:

({[{}]:{[{}]:{}}})[{}][{}] // -> {}// structure:// {//   '[object Object]': {//     '[object Object]': {}//   }// }

Read more about object literals here:
了解更多和对象文本有关的:

  • Object initializer at MDN
  • 12.2.6 Object Initializer

30. Accessing prototypes with __proto__

30. 用 __proto__访问原型

As we know, primitives don’t have prototypes. However, if we try to get a value of __proto__ for primitives, we would get this:
正如我们所知,原始类型没有原型。但是,如果我们尝试从原始类型获取一个“proto”值,我们会得到这样的结果:

(1).__proto__.__proto__.__proto__ // -> null

Explanation:
说明:

This happens because when something doesn’t have a prototype, it will be wrapped into a wrapper object using the ToObject method. So, step-by-step:
这是因为当某些东西没有原型时,将使用ToObject方法将其包装到包装对象中。所以,分解来看:

(1).__proto__ // -> [Number: 0](1).__proto__.__proto__ // -> {}(1).__proto__.__proto__.__proto__ // -> null

Here is more information about __proto__:
获取有关 __proto__的更多信息:

  • B.2.2.1 Object.prototype.proto
  • 7.1.13 ToObject(argument)

31. `${{Object}} `(最后一个反引号在代码块内)

What is the result of the expression below?
下边表达式的结果是啥?

`${{Object}}`

The answer is:
答案是:

// -> '[object Object]'

Explanation:
说明:

We defined an object with a property Object using Shorthand property notation:
我们用对象字面量简写的方法创建一个对象:

{ Object: Object }

Then we’ve passed this object to the template literal, so the toString method calls for that object. That’s why we get the string '[object Object]'.
然后我们把这个对象传入模板字符串,这样就可以为这个对象调用toString方法了。这就是为什么我们得到了字符串'[object Object]'

  • 12.2.9 Template Literals
  • Object initializer at MDN

32. Destructuring with default values

32. 解构时赋默认值

Consider this example:
思考这个例子:

let x, { x: y = 1 } = { x }; y;

The example above is a great task for an interview. What the value of y? The answer is:
上边这个例子可厉害了。所以y的值是多少?结果是:

// -> 1

Explanation:
说明:

let x, { x: y = 1 } = { x }; y;//  ↑      ↑          ↑    ↑//  1       3           2    4

With the example above:
解释一下上边的例子:

  1. We declare x with no value, so it’s undefined.
    我们声明了x,并且没有设定初始值,所以是undefined
  2. Then we pack the value of x into the object property x.
    然后我们打包x的值进了对象的属性x
  3. Then we extract the value of x using destructuring and want to assign it to y. If the value is not defined, then we’re going to use 1 as the default value.
    然后我们想把解构后的x值赋给y。如果这个值没有定义,那么我们就用1来当做默认值
  4. Return the value of y.
    返回y的值

    Object initializer at MDN

33. Dots and spreading

33. 点点和扩展

Interesting examples could be composed with spreading of arrays. Consider this:
数组的扩展有一个很有意思的例子

[...[...'...']].length // -> 3

Explanation:
说明:

Why 3? When we use the spread operator TODO(link to spec), the @@iterator method is called, and the returned iterator is used to obtain the values to be iterated. The default iterator for string spreads a string into characters. After spreading, we pack these characters into an array. Then we spread this array again and pack it back to an array.
为什么等于3?当我们用扩展操作时,会调用@@iterator方法,并且返回的迭代器是用来获取被迭代的值的。字符串默认的迭代器会把字符串展开成一个个字符。展开后,我们把这些字符打包进一个数组。然后我们再次展开这个数组然后再打包进一个数组。

A '...' string consists with three . characters, so the length of resulting array is 3.
一个'...'字符串包含三个. 字符,所以数组的长度值为3

Now, step-by-step:
现在来做分解动作:

[...'...']             // -> [ '.', '.', '.' ][...[...'...']]        // -> [ '.', '.', '.' ][...[...'...']].length // -> 3

Obviously, we can spread and wrap the elements of an array as many times as we want:
很显然,我们想这样重复多少次都可以:

[...'...']                 // -> [ '.', '.', '.' ][...[...'...']]            // -> [ '.', '.', '.' ][...[...[...'...']]]       // -> [ '.', '.', '.' ][...[...[...[...'...']]]]  // -> [ '.', '.', '.' ]// and so on …

34. Labels

34. 标记

Not so many programmers know about labels in JavaScript. They are kind of interesting:
不是很经常有程序员知道JavaScript中有标记这个东西。他还真有点意思:

foo: {  console.log('first');  break foo;  console.log('second');}// > first// -> undefined

Explanation:
说明:

The labeled statement is used with break or continue statements. You can use a label to identify a loop, and then use the break or continue statements to indicate whether a program should interrupt the loop or continue its execution.
标记过的语句与breakcontinue一起使用,你可以用标记去识别一个循环,然后用breakcontinue来识别一个程序是否该中断这个循环或者继续执行。

In the example above, we identify a label foo. After that console.log('first'); executes and then we interrupt the execution.
在上边的例子中,我们识别了标签foo。在 console.log('first');之后执行了它,然后我们中断了循环

Read more about labels in JavaScript:
了解更多JavaScript中关于labels的知识:

  • 13.13 Labelled Statements
  • Labeled statements at MDN

35. Nested labels

35. 嵌套的标签

a: b: c: d: e: f: g: 1, 2, 3, 4, 5; // -> 5

Explanation:
说明:

Similar to previous examples, follow these links:
和之前的一些例子相同:阅读下边这些链接:

  • 12.16 Comma Operator (,)
  • 13.13 Labelled Statements
  • Labeled statements at MDN

36. Insidious try..catch

36. 阴险的try..catch

What will this expression return? 2 or 3?
这个表达式会返回什么?2 还是 3

(() => {  try {    return 2;  } finally {    return 3;  }})()

The answer is 3. Surprised?
答案是3。惊讶不?

Explanation:
说明:

  • 13.15 The try Statement

37. Is this multiple inheritance?

37. 这是多重继承吗?

Take a look at the example below:
看一下下边的例子:

new (class F extends (String, Array) { }) // -> F []

Is this a multiple inheritance? Nope.
这是一个多重继承么?不是。

Explanation:
说明:

The interesting part is the value of the extends clause ((String, Array)). The grouping operator always returns its last argument, so (String, Array) is actually just Array. That means we’ve just created a class which extends Array.
有趣的是extends的值延伸自((String, Array))。组合操作符总是返回他的最后一个参数,所以(String, Array) 其实只是 Array。这意味着我们只是创建了一个扩展自Array的类。

  • 14.5 Class Definitions
  • 12.16 Comma Operator (,)

38. A generator which yields itself

38. yield自己的生成器

Consider this example of a generator which yields itself:
思考这样一个例子,一个生成器yield自己:

(function* f() { yield f })().next()// -> { value: [GeneratorFunction: f], done: false }

As you can see, the returned value is an object with its value equal to f. In that case, we can do something like this:
就像看到的一样,返回值是一个对象,这个对象的值和f相等。在这种情况下,我们可以这样做:

(function* f() { yield f })().next().value().next()// -> { value: [GeneratorFunction: f], done: false }// and again(function* f() { yield f })().next().value().next().value().next()// -> { value: [GeneratorFunction: f], done: false }// and again(function* f() { yield f })().next().value().next().value().next().value().next()// -> { value: [GeneratorFunction: f], done: false }// and so on// …

Explanation:
说明:

To understand why this works that way, read these sections of the specification:
理解为什么会有这样的结果,阅读规范中的这些部分:

  • 25 Control Abstraction Objects
  • 25.3 Generator Objects

39. A class of class

39. 一类的类

Consider this obfuscated syntax playing:
思考一下这个混乱的语法游戏:

(typeof (new (class { class () {} }))) // -> 'object'

It seems like we’re declaring a class inside of class. Should be and error, however, we get the string 'object'.
看起来像是我们在一个类中声明了一个类。应该会得到一个错误,但是并没有,我们得到一个字符串object

Explanation:
说明:

Since ECMAScript 5 era, keywords are allowed as property names. So think about it as about this simple object example:
ECMAScript 5时代以来,关键词可以是属性名称。所以考虑一下这个简单的对象示例:

const foo = {  class: function() {}};

And ES6 standardized shorthand method definitions. Also, classes can be anonymous. So if we drop : function part, we’re going to get:
这是ES6标准的字面量简写方法定义的。并且,类可以是匿名的。因此,如果我们放弃 : function部分,我们将得到:

class {  class() {}}

The result of a default class is always a simple object. And its typeof should return 'object'.
默认类的结果通常都是一个简单的对象。并且它的类型应该返回 'object'

Read more here:
了解更多:

  • 14.3 Method Definitions
  • 14.5 Class Definitions

40. Non-coercible objects

40. 非强制转换对象

With well-known symbols, there’s a way to get rid of type coercion. Take a look:
有了众所周知的符号,就有一种摆脱类型强制的方法。看一下吧。

function nonCoercible(val) {  if (val == null) {    throw TypeError('nonCoercible should not be called with null or undefined')  }  const res = Object(val)  res[Symbol.toPrimitive] = () => {    throw TypeError('Trying to coerce non-coercible object')  }  return res}

Now we can use this like this:
现在我们可以这样用:

// objectsconst foo = nonCoercible({foo: 'foo'})foo * 10      // -> TypeError: Trying to coerce non-coercible objectfoo + 'evil'  // -> TypeError: Trying to coerce non-coercible object// stringsconst bar = nonCoercible('bar')bar + '1'                 // -> TypeError: Trying to coerce non-coercible objectbar.toString() + 1        // -> bar1bar === 'bar'             // -> falsebar.toString() === 'bar'  // -> truebar == 'bar'              // -> TypeError: Trying to coerce non-coercible object// numbersconst baz = nonCoercible(1)baz == 1             // -> TypeError: Trying to coerce non-coercible objectbaz === 1            // -> falsebaz.valueOf() === 1  // -> true

Explanation:
说明:

  • A gist by Sergey Rubanov
  • 6.1.5.1 Well-Known Symbols

41. Tricky arrow functions

41. 搞怪的箭头功能

Consider the example below:
思考一下这个例子:

let f = () => 10f() // -> 10Okay, fine, but what about this:let f = () => {}f() // -> undefined

Explanation:
说明:

You might expect {} instead of undefined. This is because the curly braces are part of the syntax of the arrow functions, so f will return undefined.
比起undefined你也许更想见到{}。这是因为大括号是箭头函数语法的一部分,所以f会返回undefined。

42. Tricky return

42. 狡猾的return

return statement is also tricky. Consider this:
return 同样很狡猾。思考这段代码:

(function () {  return  {    b : 10  }})() // -> undefined

Explanation:
说明:

return and the returned expression must be in the same line:
return和返回的表达式必须在同一行:

(function () {  return {    b : 10  }})() // -> { b: 10 }

43. Accessing object properties with arrays

43. 使用数组访问对象属性

var obj = { property: 1 }var array = ['property']obj[array] // -> 1

What about pseudo-multidimensional arrays?
换做伪多维数组呢?

var map = {}var x = 1var y = 2var z = 3map[[x, y, z]] = truemap[[x + 10, y, z]] = truemap["1,2,3"]  // -> truemap["11,2,3"] // -> true

Explanation:
说明:

The brackets [] operator converts the expression passed toString. Converting an one-element array to string it’s like converting the element to the string:
方括号将表达式通过toString进行转换。将一个只有一个元素的数组转换为字符串,就像把元素转换为字符串一样。

['property'].toString() // -> 'property'

Other resources

其他资源

  • wtfjs.com — a collection of those very special irregularities, inconsistencies and just plain painfully unintuitive moments for the language of the web.
  • Wat — A lightning talk by Gary Bernhardt from CodeMash 2012
  • What the… JavaScript? — Kyle Simpsons talk for Forward 2 attempts to “pull out the crazy” from JavaScript. He wants to help you produce cleaner, more elegant, more readable code, then inspire people to contribute to the open source community.

历经四天翻译完了,宝宝有一些累,需要一杯奶茶zZZ

原创粉丝点击