一个有趣和棘手的JavaScript示例列表。
基于个人理解加google翻译,如有问题请指正,谢谢。
JavaScript是一种很好的语言。它有一个简单的语法,庞大的生态系统,以及最重要,最伟大的社区。
同时,我们都知道,JavaScript是一个非常有趣的语言,具有棘手的部分。 他们中的一些可以迅速将我们的日常工作变成地狱,有些可以让我们大声笑起来。
WTFJS的原创思想属于 Brian Leroux. 这个列表受到他的讲话的高度启发 “WTFJS” at dotJS 2012:
目录
- 💪🏻 动机
- ✍🏻 符号
-
例子
[]
等于![]
- true is false
- baNaNa
NaN
不是一个NaN
- It's a fail
[]
本身是true, 但又不等于true
null
本身是false, 但又不等于false
- 最小值大于零
- 函数又不是函数
- 数组相加
undefined
和Number
parseInt
是一个坏蛋- 数学计算中
true
和false
- HTML注释在JavaScript中有效
NaN
不是一个数值[]
和null
都是对戏那个- 神奇的数字
- 精度问题
0.1 + 0.2
- 修复数字
- 三个数字的比较
- 有趣的数学
- 添加正则表达式
- 字符串不是
String
的实例 - 用反引号调用函数
- 调用 调用 调用
- 一个
constructor
属性 - 将对象做为另一个对象的key
- 用
__proto__
访问原型 `${{Object}}`
- 使用默认值进行结构化
- 点 和 解构
- 标签
- 嵌套标签
- 阴险的
try..catch
- 这是多重继承吗?
- A generator which yields itself
- 一个类的类
- 非强制对象
- License
💪🏻 动机
只是为了好玩 (tips:我翻译这篇文章同样也是为了好玩)
— “只是为了好玩:一个意外革命的故事”, 托瓦兹
这个列表的主要目的是收集一些疯狂的例子,并解释它们如何工作,如果可能的话。 只是因为学习以前不了解的东西很有趣。
如果您是初学者,您可以使用此笔记来深入了解JavaScript。 我希望这个笔记会激励你花更多的时间阅读规范。
如果您是专业开发人员,您可以将这些示例视为您公司新手访问问题和测验的重要资源。 同时,这些例子在准备面试时会很方便。
无论如何,只是读这个。 也许你会为自己找到新的东西。
✍🏻 符号
// ->
用于显示表达式的结果。 例如:
1 + 1 // -> 2
// >
意思是 console.log
或其他输出的结果。 例如:
console.log('hello, world!') // > hello, world!
//
只是一个解释的评论。 例:
// Assigning a function to foo constant
const foo = function () {}
例子
[]
等于 ![]
数组等于一个数组取反
[] == ![] // -> true
说明:
true 是 false
!!'false' == !!'true' // -> true
!!'false' === !!'true' // -> true
说明:
考虑一下这一步:
true == 'true' // -> true
false == 'false' // -> false
// 'false' 不是空字符串,所以它的值是true
!!'false' // -> true
!!'true' // -> true
baNaNa
'b' + 'a' + + 'a' + 'a'
用JavaScript写的老派笑话:
"foo" + + "bar" // -> 'fooNaN'
说明:
这个表达式可以转化成 'foo' + (+'bar')
,但无法将'bar'
强制转化成数值
NaN
不是一个 NaN
NaN === NaN // -> false
说明:
规范严格定义了这种行为背后的逻辑:
- 如果
Type(x)
不同于Type(y)
, return false.- 如果
Type(x)
数值, 然后
- 如果
x
是 NaN, return false.- 如果
y
是 NaN, return false.- … … …
遵循IEEE的“NaN”的定义:
四个相互排斥的关系是可能的:小于,等于,大于和无序。 当至少一个操作数是NaN时,最后一种情况出现。 每个NaN都要比较无穷无尽的一切,包括自己。
— “对于IEEE754 NaN值的所有比较返回false的理由是什么?” at StackOverflow
它是fail
你不会相信,但...
(![]+[])[+[]]+(![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]
// -> 'fail'
说明:
将大量的符号分解成片段,我们注意到,以下表达式经常发生:
(![]+[]) // -> 'false'
![] // -> false
所以我们尝试将[]
和false
加起来。 但是通过一些内部函数调用(binary + Operator
- >ToPrimitive
- >[[DefaultValue]
]),我们最终将右边的操作数转换为一个字符串:
(![]+[].toString()) // 'false'
将字符串作为数组,我们可以通过[0]
来访问它的第一个字符:
'false'[0] // -> 'f'
现在,其余的是明显的,可以自己弄清楚!
[]
是 true
, 但它不等于 true
数组是一个true
,但是它不等于true
。
!![] // -> true
[] == true // -> false
说明:
以下是ECMA-262规范中相应部分的链接:
null
是false, 但又不等于 false
尽管 null
是 false
,但它不等于 false
。
!!null // -> false
null == false // -> false
同时,其他的一些等于false的值,如 0
或 ''
等于 false
。
0 == false // -> true
'' == false // -> true
说明:
跟前面的例子相同。 这是一个相应的链接:
最小值大于零
Number.MIN_VALUE
是最小的数字,大于零:
Number.MIN_VALUE > 0 // -> true
说明:
Number.MIN_VALUE
是5e-324
,即可以在浮点精度内表示的最小正数,即可以达到零。 它定义了最好的分辨率浮标给你。
现在,整体最小的值是
Number.NEGATIVE_INFINITY
,尽管这在严格意义上并不是真正的数字。— “为什么在JavaScript中
0
小于Number.MIN_VALUE
?” at StackOverflow
函数不是函数
V8 v5.5或更低版本中出现的Bug(Node.js <= 7)
所有你知道的关于噪声 undefined不是function 。是关于这个吗?
// Declare a class which extends null
class Foo extends null {}
// -> [Function: Foo]
new Foo instanceof null
// > TypeError: function is not a function
// > at … … …
说明:
这不是规范的一部分。这只是一个错误,现在它是固定的,所以将来不会有这个问题。
数组相加
如果您尝试两个数组相加呢?
[1, 2, 3] + [4, 5, 6] // -> '1,2,34,5,6'
说明:
会发生合并。一步一步地,它是这样的:
[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个空元素的数组。尽管如此,你还是会得到一个有三个元素的,因为后面的逗号:
let a = [,,,]
a.length // -> 3
a.toString() // -> ',,'
说明:
尾逗号 (有时也称为“最后逗号”) 在向JavaScript代码中添加新元素、参数或属性时有用。如果您想添加一个新属性,您可以简单地添加一个新行,而不用修改以前的最后一行,如果该行已经使用了后面的逗号。这使得版本控制比较清洁和编辑代码可能不太麻烦。
— Trailing commas at MDN
数组相等是一个怪物
数组进行相等比较是一个怪物,看下面的例子:
[] == '' // -> 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 规范部分。
undefined
和 Number
如果我们不把任何参数传递到 Number
构造函数中,我们将得到 0
。undefined
是一个赋值形参,没有实际的参数,所以您可能期望 NaN
将 undefined
作为参数的值。然而,当我们通过 undefined
,我们将得到 NaN
。
Number() // -> 0
Number(undefined) // -> NaN
说明:
根据规格:
- 如果没有参数传递给这个函数,让
n
为+0
; - 否则,让
n
调用ToNumber(value)
- 如果值为
undefined
,那么ToNumber(undefined)
应该返回NaN
.
这是相应的部分:
parseInt
是一个坏蛋
parseInt
它以的怪异而出名。
parseInt('f*ck'); // -> NaN
parseInt('f*ck', 16); // -> 15
** 说明:
** 这是因为 parseInt
会继续通过解析直到它解析到一个不识别的字符,f
在 'fuck'
是 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'
,并尝试转换它。 对于基数0到23,没有可以转换的数字,因此返回NaN。 在24,“n”
,第14个字母被添加到数字系统。 在31,“u”
,添加第21个字母,可以解码整个字符串。 在37处,不再有可以生成的有效数字集,并返回NaN
。— “parseInt(null, 24) === 23… wait, what?” at StackOverflow
不要忘记八进制:
parseInt('06'); // 6
parseInt('08'); // 0
说明:
这是因为 parseInt
能够接受两个参数,如果没有提供第二个参数,并且第一个参数以 0
开始,它将被解析为八进制数。
true
和 false
数学运算
我们做一些数学计算:
true + true // -> 2
(true + true) * (true + true) - true // -> 3
嗯…
说明:
我们可以用 Number
构造函数强制转化成数值。 很明显,true
将被强制转换为 1
:
Number(true) // -> 1
一元加运算符尝试将其值转换成数字。 它可以转换整数和浮点的字符串表示,以及非字符串值 true
,false
和 null
。 如果它不能解析特定的值,它将转化为 NaN
。 这意味着我们可以更容易地强制将 true
换成 1
+true // -> 1
当你执行加法或乘法时,ToNumber
方法调用。 根据规范,该方法返回:
如果
参数
is true , 返回 1 。 如果参数
是 false 返回 +0。
这就是为什么我们可以进行进行布尔值相加并得到正确的结果
相应部分:
HTML注释在JavaScript中有效
你会留下深刻的印象, <!--
(这被称为HTML注释)是一个有效的JavaScript注释。
// valid comment
<!-- valid comment too
说明:
深刻的印象吗? html类似的注释旨在允许不理解标签的浏览器优雅地降级。这些浏览器,例如Netscape 1。x不再受欢迎。因此,在脚本标记中添加HTML注释是没有意义的。
由于Node.js基于V8引擎,Node.js运行时也支持类似HTML的注释。 而且,它们是规范的一部分:
NaN
不是一个数值
尽管 NaN
类型是 'number'
,但是 NaN
不是数字的实例:
typeof NaN // -> 'number'
NaN instanceof Number // -> false
说明:
typeof
和 instanceof
运算符的工作原理:
[]
和 null
是对象
typeof [] // -> 'object'
typeof null // -> 'object'
// 然而
null instanceof Object // false
说明:
typeof
运算符的行为在本节的规范中定义:
根据规范,typeof
操作符返回一个字符串 Table 35: typeof
Operator Results. 对于 null
,普通的,标准的异常和非标准的异常对象,它不实现 [[Call]]
它返回字符串 "对象“
。
但是,您可以使用 toString
方法检查对象的类型。
Object.prototype.toString.call([])
// -> '[object Array]'
Object.prototype.toString.call(new Date)
// -> '[object Date]'
Object.prototype.toString.call(null)
// -> '[object Null]'
神奇多位的数字
999999999999999 // -> 999999999999999
9999999999999999 // -> 10000000000000000
说明:
这是由IEEE 754-2008二进制浮点运算标准引起的。 阅读更多:
- 6.1.6 The Number Type
- IEEE 754 on Wikipedia
0.1 + 0.2
精度计算
来自JavaScript的知名笑话 0.1
和 0.2
相加是存在精度错误的
0.1 + 0.2 // -> 0.30000000000000004
说明:
答案为 ”浮点数学是坏的?” 问题在StackOverflow:
程序中的常量
0.2
和0.3
也将近似为真值。 发生最接近的double
到0.2
大于有理数0.2
,但最接近的double
到0.3
小于有理数0.3
。0.1
和0.2
的总和大于理性数0.3
,因此不符合您的代码中的常数。
这个问题是众所周知的,甚至有一个网站叫 0.30000000000000004.com.
扩展数字的方法
您可以添加自己的方法来包装对象,如 Number
或 String
。
Number.prototype.isOne = function () {
return Number(this) === 1
}
1.0.isOne() // -> true
1..isOne() // -> true
2.0.isOne() // -> false
(7).isOne() // -> false
说明:
显然,您可以像JavaScript中的任何其他对象一样扩展 Number
对象。 但是,如果定义的方法的行为不是规范的一部分,则不建议。 以下是 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 > 2 >= 1 // true
详细了解规范中的关系运算符:
有趣的数学
通常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
说明:
前四个例子发生了什么? 这是一个小表,以了解JavaScript中的添加:
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 (
+
) - 7.1.1 ToPrimitive(
input
[,PreferredType
]) - 7.1.12 ToString(
argument
)
扩展正则
你知道可以做这样的运算吗?
// Patch a toString method
RegExp.prototype.toString = function() {
return this.source
}
/7/ - /5/ // -> 2
说明:
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']
有关规范中的String构造函数的更多信息:
用反引号调用函数
我们来声明一个返回所有参数到控制台中的函数:
function f(...args) {
return args
}
毫无疑问,你知道你可以这样调用这个函数:
f(1, 2, 3) // -> [ 1, 2, 3 ]
但是你知道你可以使用反引号来调用任何函数吗?
f`true is ${true}, false is ${false}, array is ${[1,2,3]}`
// -> [ [ 'true is ', ', false is ', ', array is ', '' ],
// -> true,
// -> false,
// -> [ 1, 2, 3 ] ]
说明:
那么,如果你熟悉 Tagged模板文字 ,这根本就不是魔术。 在上面的例子中,f
函数是模板文字的标签。 模板文字之前的标签允许您使用函数解析模板文字。 标签函数的第一个参数包含字符串值的数组。 其余的参数与表达式有关。 例:
function template(strings, ...keys) {
// 用字符串和键做一些事情
}
This is the magic behind famous library called styled-components, which is popular in React community.
链接到规范:
调用 调用 调用
发现于 @cramforce
console.log.call.call.call.call.call.apply(a => a, [1, 2])
说明:
注意,可能会打破你的头脑! 尝试在您的头脑中重现此代码:我们使用apply
方法应用call
方法。 阅读更多:
- 19.2.3.3 Function.prototype.call(
thisArg
, ...args
) - **19.2.3.1 ** Function.prototype.apply(
thisArg
,argArray
)
constructor
属性
一个const c = 'constructor'
c[c][c]('console.log("WTF?")')() // > WTF?
说明:
让我们逐步考虑一下这个例子:
// Declare a new constant which is a string 'constructor'
const c = 'constructor'
// c 是一个字符串
c // -> 'constructor'
// 获取字符串的构造函数
c[c] // -> [Function: String]
// 获取构造函数的构造函数
c[c][c] // -> [Function: Function]
// 调用函数构造函数并将新函数的主体作为参数传递
c[c][c]('console.log("WTF?")') // -> [Function: anonymous]
// 然后调用这个匿名函数得到的结果是一个字符串'WTF'
c[c][c]('console.log("WTF?")')() // > WTF
一个 Object.prototype.constructor
返回一个引用对象的构造函数创建的实例对象。 在字符串的情况下,它是 String
,在数字的情况下它是 数字
等等。
将对象做为另一个对象的key
{ [{}]: {} } // -> { '[object Object]': {} }
说明:
为什么这样工作? 这里我们使用 计算属性的名称 TODO(添加链接到spec)。 当这些方括号之间传递一个对象时,它会将对象强制转换成一个字符串,所以我们得到一个属性键 [object Object]
和 值是 {}
。
同样的,我们也可以这样:
({[{}]:{[{}]:{}}})[{}][{}] // -> {}
// structure:
// {
// '[object Object]': {
// '[object Object]': {}
// }
// }
阅读更多关于对象litarals这里:
- Object initializer at MDN
__proto__
访问原型
正如我们所知道的,原本是没有原型。但是,如果我们尝试为原始对象获取一个 __proto__
的值,我们会得到这样的一个结果:
(1).__proto__.__proto__.__proto__ // -> null
说明:
这是因为原本的没有原型,它将使用 ToObject
方法包装在包装器对象中。 所以,一步一步:
(1).__proto__ // -> [Number: 0]
(1).__proto__.__proto__ // -> {}
(1).__proto__.__proto__.__proto__ // -> null
以下是关于 __proto__
的更多信息:
`${{Object}}`
下面的表达结果如何?
`${{Object}}`
答案是:
// -> '[object Object]'
说明:
我们使用一个属性 object
定义了一个对象:
{ Object: Object }
然后我们将该对象传递给模板文字,因此 toString
方法调用该对象。 这就是为什么我们得到字符串 '[object Object]'
。
使用默认值进行结构化
思考这个例子:
let x, { x: y = 1 } = { x }; y;
上面的例子是访问的一个很好的任务。 y
有什么值? 答案是:
// -> 1
说明:
let x, { x: y = 1 } = { x }; y;
// ↑ ↑ ↑ ↑
// 1 3 2 4
以上示例:
- 我们声明
x
没有赋值,所以它是'undefined`。 - 然后我们将
x
的值打包到对象属性x
中。 - 然后我们使用解构来提取
x
的值,并且要将这个值分配给y
。 如果未定义该值,那么我们将使用1
作为默认值。 - 返回
y
的值。
- Object initializer at MDN
点 和 解构
有趣的例子可以由阵列的扩展组成。 考虑这个:
[...[...'...']].length // -> 3
说明:
为什么是3?当我们使用扩展运算符 TODO链接到规范)时,@@iterator
方法调用,而返回的迭代器用于获取要迭代的值。字符串的默认迭代器按字符传播字符串。传播之后,我们把这些字符打包成一个数组。然后再展开这个数组并打包回数组
一个 '...'
字符串包含 .
,所以结果数组的长度将' 3 '。
现在,一步一步的看
[...'...'] // -> [ '.', '.', '.' ]
[...[...'...']] // -> [ '.', '.', '.' ]
[...[...'...']].length // -> 3
显然,我们可以像我们想要的那样传播和包装数组的元素:
[...'...'] // -> [ '.', '.', '.' ]
[...[...'...']] // -> [ '.', '.', '.' ]
[...[...[...'...']]] // -> [ '.', '.', '.' ]
[...[...[...[...'...']]]] // -> [ '.', '.', '.' ]
// and so on …
标签
很多程序员不知道JavaScript中的标签。 但它们很实用
foo: {
console.log('first');
break foo;
console.log('second');
}
// > first
// -> undefined
说明:
带标签的语句与 break
或 continue
语句一起使用。 您可以使用标签来标识循环,然后使用 break
或 continue
语句来指示程序是否应该中断循环或继续执行它。
在上面的例子中,我们识别一个标签 foo
。 然后 console.log('first');
执行,然后中断执行。
详细了解JavaScript中的标签:
- 13.13 标签语句
- 标签语句 at MDN
嵌套标签
a: b: c: d: e: f: g: 1, 2, 3, 4, 5; // -> 5
说明:
像以前的例子一样,请遵循以下链接:
- 12.16 逗号运算符(
,
) - 13.13 标签语句
- 标签语句 at MDN
try..catch
阴险的
这个表达将返回什么? 2
或 3
?
(() => {
try {
return 2;
} finally {
return 3;
}
})()
答案是3。惊讶吗?
说明:
这是多重继承吗?
看下面的例子:
new (class F extends (String, Array) { }) // -> F []
这是多重继承吗? 不。
说明:
有趣的部分是 extends
子句的值( (String,Array)
)。 分组运算符总是返回其最后一个参数,所以 (String,Array)
实际上只是 Array
。 这意味着我们刚刚创建了一个扩展 Array
的类。
A generator which yields itself
考虑一下这个例子,它产生了一个生成器:
(function* f() { yield f })().next()
// -> { value: [GeneratorFunction: f], done: false }
如您所见,返回的值是一个值等于 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
// …
说明:
要理解为什么这样工作,请阅读规范的这些部分:
一个类的类
考虑这个混淆的语法游戏:
(typeof (new (class { class () {} }))) // -> 'object'
似乎我们在类内部声明了一个类。应该和错误,然而,我们得到一个 'object'
字符串。
说明:
ECMAScript 5时代以来,关键字允许访问属性。所以请考虑一下这个简单的对象示例:
const foo = {
class: function() {}
};
和6标准速记方法定义。此外,类可能是匿名的。因此,如果我们放弃 函数
部分,我们将得到:
class {
class() {}
}
默认类的结果总是一个简单的对象。其类型应返回 'object'
。
在这里阅读更多
非强制对象
有着名的符号,有一种方法可以摆脱类型的强制。 看一看:
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
}
现在我们可以这样使用:
// objects
const foo = nonCoercible({foo: 'foo'})
foo * 10 // -> TypeError: Trying to coerce non-coercible object
foo + 'evil' // -> TypeError: Trying to coerce non-coercible object
// strings
const bar = nonCoercible('bar')
bar + '1' // -> TypeError: Trying to coerce non-coercible object
bar.toString() + 1 // -> bar1
bar === 'bar' // -> false
bar.toString() === 'bar' // -> true
bar == 'bar' // -> TypeError: Trying to coerce non-coercible object
// numbers
const baz = nonCoercible(1)
baz == 1 // -> TypeError: Trying to coerce non-coercible object
baz === 1 // -> false
baz.valueOf() === 1 // -> true
说明:
棘手的箭头功能
考虑下面的例子:
let f = () => 10
f() // -> 10
好吧,但是这是怎么说的呢?
let f = () => {}
f() // -> undefined
说明:
你可以期待 {}
而不是 undefined
。这是因为花括号是箭头函数语法的一部分,所以f会返回未定义的。
棘手的返回
return
声明是很棘手的. 看下面的代码:
(function () {
return
{
b : 10
}
})() // -> undefined
说明:
return
一个表达式必须在同一行:
(function () {
return {
b : 10
}
})() // -> { b: 10 }
使用数组访问对象属性
var obj = { property: 1 }
var array = ['property']
obj[array] // -> 1
那关于维多维数组创建对象呢?
var map = {}
var x = 1
var y = 2
var z = 3
map[[x, y, z]] = true
map[[x + 10, y, z]] = true
map["1,2,3"] // -> true
map["11,2,3"] // -> true
说明:
括号操作符将传递给字符串的表达式转换为字符串。将一个元素数组转换为字符串,就像将元素转换为字符串:
['property'].toString() // -> 'property'`
其他资源
- wtfjs.com — 这是一组非常特别的不规范,不一致的地方,以及那些对于网络语言来说非常痛苦的不直观的时刻。
- Wat — A lightning talk by Gary Bernhardt from CodeMash 2012
- What the... JavaScript? — 凯尔。辛普森一家谈到了前两次试图从JavaScript中“拉出疯狂”的尝试。他希望帮助您生成更干净、更优雅、更可读的代码,然后鼓励人们为开源社区做出贡献。