# wtfjs **Repository Path**: liduanjun/wtfjs ## Basic Information - **Project Name**: wtfjs - **Description**: A list of funny and tricky JavaScript examples - **Primary Language**: JavaScript - **License**: WTFPL - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-11-03 - **Last Updated**: 2021-11-03 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # What the f\*ck JavaScript? [![WTFPL 2.0][license-image]][license-url] [![NPM version][npm-image]][npm-url] > 一个有趣和棘手的 JavaScript 示例列表。 基于个人理解加 google 翻译,如有问题请指正,谢谢。 JavaScript 是一种很好的语言。它有一个简单的语法,庞大的生态系统,以及最重要,最伟大的社区。 同时,我们都知道,JavaScript 是一个非常有趣的语言,具有棘手的部分。 他们中的一些可以迅速将我们的日常工作变成地狱,有些可以让我们大声笑起来。 WTFJS 的原创思想属于 [Brian Leroux](https://twitter.com/brianleroux). 这个列表受到他的讲话的高度启发 [**“WTFJS”** at dotJS 2012](https://www.youtube.com/watch?v=et8xNAc2ic8): [![dotJS 2012 - Brian Leroux - WTFJS](https://img.youtube.com/vi/et8xNAc2ic8/0.jpg)](https://www.youtube.com/watch?v=et8xNAc2ic8) # npm 手稿 你可以通过 `npm` 来安装。只要运行: ``` $ npm install -g wtfjs ``` 你应该能够在命令行中运行`wtfjs`,这将打开手册并在你选择的`$PAGER`中,否则你也可以选择在这里阅读。 # 目录 - [💪🏻 动机](#-动机) - [✍🏻 符号](#-符号) - [👀 例子](#-例子) - [`[]` 等于 `![]`](#-等于-) - [true is false](#true-是-false) - [baNaNa](#baNaNa) - [`NaN` 不是一个 `NaN`](#nan-不是一个-nan) - [It's a fail](#它是fail) - [`[]` 本身是 true, 但又不等于 `true`](#-是-true-但它不等于-true) - [`null` 本身是 false, 但又不等于 `false`](#null-是false-但又不等于-false) - [最小值大于零](#最小值大于零) - [函数又不是函数](#函数又不是函数) - [数组相加](#数组相加) - [`undefined` 和 `Number`](#undefined-和-number) - [`parseInt` 是一个坏蛋 ](#parseint-是一个坏蛋) - [数学计算中 `true` 和 `false`](#true-和-false-数学运算) - [HTML 注释在 JavaScript 中有效](#html注释在javascript中有效) - [`NaN` ~~不是~~ 一个数值](#nan-不是一个数值) - [`[]` 和 `null` 都是对戏那个](#-和-null-是对象) - [神奇的数字](#神奇多位的数字) - [精度问题 `0.1 + 0.2`](#01--02-精度计算) - [修复数字](#扩展数字的方法) - [三个数字的比较](#三个数字的比较) - [有趣的数学](#有趣的数学) - [添加正则表达式](#扩展正则) - [字符串不是 `String` 的实例](#字符串不是-string-的实例) - [用反引号调用函数](#用反引号调用函数) - [调用 调用 调用](#调用-调用-调用) - [一个 `constructor` 属性](#一个constructor属性) - [将对象做为另一个对象的 key](#将对象做为另一个对象的key) - [用`__proto__`访问原型](#访问原型-__proto__) - [`` `${{Object}}` ``](#-object-) - [使用默认值进行结构化](#使用默认值进行结构化) - [点 和 解构](#点-和-解构) - [标签](#标签) - [嵌套标签](#嵌套标签) - [阴险的 `try..catch`](#阴险的-trycatch) - [这是多重继承吗?](#这是多重继承吗) - [A generator which yields itself](#a-generator-which-yields-itself) - [一个类的类](#一个类的类) - [非强制转换对象](#非强制转换对象) - [棘手的箭头功能](#棘手的箭头功能) - [`arguments`和箭头函数](#arguments-和箭头函数) - [棘手的返回](#棘手的返回) - [使用数组访问对象属性](#使用数组访问对象属性) - [Null 和关系操作符](#空和关系操作符) - [`Number.toFixed()` 显示不同的数字](#numbertofixed-显示不同的数字) - [比较 `null` to `0`](#比较-null-to-0) - [其他资源](#其他资源) - [🎓 License](#-license) # 💪🏻 动机 > 只是为了好玩 (tips:我翻译这篇文章同样也是为了好玩) > > — _[**“只是为了好玩:一个意外革命的故事”**](https://en.m.wikipedia.org/wiki/Just_for_Fun), 托瓦兹_ 这个列表的主要目的是收集一些疯狂的例子,并解释它们如何工作,如果可能的话。 只是因为学习以前不了解的东西很有趣。 如果您是初学者,您可以使用此笔记来深入了解 JavaScript。 我希望这个笔记会激励你花更多的时间阅读规范。 如果您是专业开发人员,您可以将这些示例视为您公司新手访问问题和测验的重要资源。 同时,这些例子在准备面试时会很方便。 无论如何,只是读这个。 也许你会为自己找到新的东西。 # ✍🏻 符号 **`// ->`** 用于显示表达式的结果。 例如: ```js 1 + 1; // -> 2 ``` **`// >`** 意思是 `console.log` 或其他输出的结果。 例如: ```js console.log("hello, world!"); // > hello, world! ``` **`//`** 只是一个解释的评论。 例: ```js // Assigning a function to foo constant const foo = function() {}; ``` # 👀 例子 ## `[]` 等于 `![]` 数组等于一个数组取反 ```js [] == ![]; // -> true ``` ### 💡 说明: - [**12.5.9** 逻辑非运算符 (`!`)](https://www.ecma-international.org/ecma-262/#sec-logical-not-operator) - [**7.2.13** 抽象相等比较 ](https://www.ecma-international.org/ecma-262/#sec-abstract-equality-comparison) ## true 是 false ```js !!"false" == !!"true"; // -> true !!"false" === !!"true"; // -> true ``` ### 💡 说明: 考虑一下这一步: ```js true == "true"; // -> true false == "false"; // -> false // 'false' 不是空字符串,所以它的值是true !!"false"; // -> true !!"true"; // -> true ``` - [**7.2.13** 抽象相等比较 ](https://www.ecma-international.org/ecma-262/#sec-abstract-equality-comparison) ## baNaNa ```js "b" + "a" + +"a" + "a"; ``` 用 JavaScript 写的老派笑话: ```js "foo" + +"bar"; // -> 'fooNaN' ``` ### 💡 说明: 这个表达式可以转化成 `'foo' + (+'bar')`,但无法将`'bar'`强制转化成数值 - [**12.8.3** 加法运算符 (`+`)](https://www.ecma-international.org/ecma-262/#sec-addition-operator-plus) ## `NaN` 不是一个 `NaN` ```js NaN === NaN; // -> false ``` ### 💡 说明: 规范严格定义了这种行为背后的逻辑: > 1. 如果 `Type(x)` 不同于 `Type(y)`, return **false**. > 2. 如果 `Type(x)` 数值, 然后 > 1. 如果 `x` 是 **NaN**, return **false**. > 2. 如果 `y` 是 **NaN**, return **false**. > 3. … … … > > — [**7.2.14** 严格模式相等比较 ](https://www.ecma-international.org/ecma-262/#sec-strict-equality-comparison) 遵循 IEEE 的“NaN”的定义: > 四个相互排斥的关系是可能的:小于,等于,大于和无序。 当至少一个操作数是 NaN 时,最后一种情况出现。 每个 NaN 都要比较无穷无尽的一切,包括自己。 > > — [“对于 IEEE754 NaN 值的所有比较返回 false 的理由是什么?”](https://stackoverflow.com/questions/1565164/1573715#1573715) at StackOverflow ## 它是 fail 你不会相信,但... ```js (![] + [])[+[]] + (![] + [])[+!+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]]; // -> 'fail' ``` ### 💡 说明: 将大量的符号分解成片段,我们注意到,以下表达式经常发生: ```js ![] + []; // -> 'false' ![]; // -> false ``` 所以我们尝试将`[]`和`false`加起来。 但是通过一些内部函数调用(`binary + Operator` - >`ToPrimitive` - >`[[DefaultValue]` ]),我们最终将右边的操作数转换为一个字符串: ```js ![] + [].toString(); // 'false' ``` 将字符串作为数组,我们可以通过`[0]`来访问它的第一个字符: ```js "false"[0]; // -> 'f' ``` 现在,其余的是明显的,可以自己弄清楚! ## `[]` 是 `true`, 但它不等于 `true` 数组是一个`true`,但是它不等于`true`。 ```js !![] // -> true [] == true // -> false ``` ### 💡 说明: 以下是 ECMA-262 规范中相应部分的链接: - [**12.5.9** 逻辑非运算符 (`!`)](https://www.ecma-international.org/ecma-262/#sec-logical-not-operator) - [**7.2.13** 抽象相等比较 ](https://www.ecma-international.org/ecma-262/#sec-abstract-equality-comparison) ## `null` 是 false, 但又不等于 `false` 尽管 `null` 是 `false` ,但它不等于 `false`。 ```js !!null; // -> false null == false; // -> false ``` 同时,其他的一些等于 false 的值,如 `0` 或 `''` 等于 `false` 。 ```js 0 == false; // -> true "" == false; // -> true ``` ### 💡 说明: 跟前面的例子相同。 这是一个相应的链接: - [**7.2.13** 抽象相等比较 ](https://www.ecma-international.org/ecma-262/#sec-abstract-equality-comparison) ## 最小值大于零 `Number.MIN_VALUE` 是最小的数字,大于零: ```js Number.MIN_VALUE > 0; // -> true ``` ### 💡 说明: > `Number.MIN_VALUE` 是 `5e-324` ,即可以在浮点精度内表示的最小正数,即可以达到零。 它定义了最好的分辨率浮标给你。 > 现在,整体最小的值是 `Number.NEGATIVE_INFINITY` ,尽管这在严格意义上并不是真正的数字。 > > — [“为什么在 JavaScript 中`0`小于`Number.MIN_VALUE`?”](https://stackoverflow.com/questions/26614728/why-is-0-less-than-number-min-value-in-javascript) at StackOverflow - [**20.1.2.9** Number.MIN_VALUE](https://www.ecma-international.org/ecma-262/#sec-well-known-symbols) ## 函数不是函数 > ⚠️ V8 v5.5 或更低版本中出现的 Bug(Node.js <= 7) ⚠️ 所有你知道的关于噪声 _undefined 不是 function_ 。是关于这个吗? ```js // Declare a class which extends null class Foo extends null {} // -> [Function: Foo] new Foo() instanceof null; // > TypeError: function is not a function // > at … … … ``` ### 💡 说明: 这不是规范的一部分。这只是一个错误,现在它是固定的,所以将来不会有这个问题。 ## 数组相加 如果您尝试两个数组相加呢? ```js [1, 2, 3] + [4, 5, 6]; // -> '1,2,34,5,6' ``` ### 💡 说明: 会发生合并。一步一步地,它是这样的: ```js [1, 2, 3] + [4, 5, 6][ // joining (1, 2, 3) ].join() + [4, 5, 6].join(); // concatenation "1,2,3" + "4,5,6"; // -> ("1,2,34,5,6"); ``` # 数组中的逗号 您已经创建了一个包含 4 个空元素的数组。尽管如此,你还是会得到一个有三个元素的,因为后面的逗号: ```js let a = [, , ,]; a.length; // -> 3 a.toString(); // -> ',,' ``` ### 💡 说明: > **尾逗号** (有时也称为“最后逗号”) 在向 JavaScript 代码中添加新元素、参数或属性时有用。如果您想添加一个新属性,您可以简单地添加一个新行,而不用修改以前的最后一行,如果该行已经使用了后面的逗号。这使得版本控制比较清洁和编辑代码可能不太麻烦。 > > — [Trailing commas](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Trailing_commas) at MDN ## 数组相等是一个怪物 数组进行相等比较是一个怪物,看下面的例子: ```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 ``` ### 💡 说明: 你应该非常小心,因为上面!这是一个复杂的例子,但它的描述 [**7.2.13** Abstract Equality Comparison](https://www.ecma-international.org/ecma-262/#sec-abstract-equality-comparison) 规范部分。 ## `undefined` 和 `Number` 如果我们不把任何参数传递到 `Number` 构造函数中,我们将得到 `0` 。`undefined` 是一个赋值形参,没有实际的参数,所以您可能期望 `NaN` 将 `undefined` 作为参数的值。然而,当我们通过 `undefined` ,我们将得到 `NaN` 。 ```js Number(); // -> 0 Number(undefined); // -> NaN ``` ### 💡 说明: 根据规格: 1. 如果没有参数传递给这个函数,让 `n` 为 `+0` ; 2. 否则,让 `n` 调用 `ToNumber(value)` 3. 如果值为 `undefined`,那么 `ToNumber(undefined)` 应该返回 `NaN`. 这是相应的部分: - [**20.1.1** Number 构造器函数 ](https://www.ecma-international.org/ecma-262/#sec-number-constructor) - [**7.1.3** ToNumber(`argument`)](https://www.ecma-international.org/ecma-262/#sec-tonumber) ## `parseInt` 是一个坏蛋 `parseInt` 它以的怪异而出名。 ```js parseInt("f*ck"); // -> NaN parseInt("f*ck", 16); // -> 15 ``` \*\*💡 说明: \*\* 这是因为 `parseInt` 会继续通过解析直到它解析到一个不识别的字符,`f` 在 `'fuck'` 是 `15进制` 解析 `Infinity` 到整数是什么… ```js // 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` : ```js parseInt(null, 24); // -> 23 ``` **💡 说明:** > 它将 `null` 转换成字符串 `'null'` ,并尝试转换它。 对于基数 0 到 23,没有可以转换的数字,因此返回 NaN。 在 24,`“n”` ,第 14 个字母被添加到数字系统。 在 31,`“u”` ,添加第 21 个字母,可以解码整个字符串。 在 37 处,不再有可以生成的有效数字集,并返回 `NaN` 。 > > — [“parseInt(null, 24) === 23… wait, what?”](https://stackoverflow.com/questions/6459758/parseintnull-24-23-wait-what) at StackOverflow 不要忘记八进制: ```js parseInt("06"); // 6 parseInt("08"); // 0 ``` **💡 说明:** 这是因为 `parseInt` 能够接受两个参数,如果没有提供第二个参数,并且第一个参数以 `0` 开始,它将被解析为八进制数。 ## `true` 和 `false` 数学运算 我们做一些数学计算: ```js true + true( // -> 2 true + true ) * (true + true) - true; // -> 3 ``` 嗯… 🤔 ### 💡 说明: 我们可以用 `Number` 构造函数强制转化成数值。 很明显,`true` 将被强制转换为 `1` : ```js Number(true); // -> 1 ``` 一元加运算符尝试将其值转换成数字。 它可以转换整数和浮点的字符串表示,以及非字符串值 `true` ,`false` 和 `null` 。 如果它不能解析特定的值,它将转化为 `NaN` 。 这意味着我们可以更容易地强制将 `true` 换成 `1` ```js +true; // -> 1 ``` 当你执行加法或乘法时,`ToNumber`方法调用。 根据规范,该方法返回: > 如果 `参数` is **true** , 返回 **1** 。 如果 `参数` 是 **false** 返回 **+0**。 这就是为什么我们可以进行进行布尔值相加并得到正确的结果 相应部分: - [**12.5.6** 一元 `+` 运算符 ](https://www.ecma-international.org/ecma-262/#sec-unary-plus-operator) - [**12.8.3** 加法运算符(`+`) ](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) ## HTML 注释在 JavaScript 中有效 你会留下深刻的印象, `