# js边角料

back

+0 === -0 //true
NaN === NaN // false
NaN !== NaN // true

Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
1
2
3
4
5
6
  • 1、一些假值:false,null,0,"",undefined,NaN
  • 2、使用.link() 创建链接
// --- before ---
let b = `<a herf="www.google.com">google</a>`;

// --- after ---
let b = 'google'.link('www.google.com');
//"<a href="www.google.com">google</a>"
1
2
3
4
5
6
  • 3、使用当前时间创建一个随机数
// --- before ---
let b = 0 | Math.random() * 100

// --- after ---
let a;
a = new Date % 100; // 两位随机数
a = new Date % 1000; // 三位随机数
a = new Date % 10000; // 四位随机数...依次类推
// 不要在快速循环中使用,因为毫秒可能没有变化;
1
2
3
4
5
6
7
8
9
  • 4、一些可以替代 undefined 的操作
    • ""._, 1.._0[0]
    • void 0 会比写 undefined 要快一些
  • 5、使用 1/0 来替代 Infinity
// --- before ---
[Infinity, -Infinity]

// --- after ---
[1/0, -1/0]
1
2
3
4
5
  • 6、使用 !+"\v1" 快速判断 IE8 以下的浏览器
  • 7、for 循环条件的简写
// --- before ---
for(let i = 0; i < arr.length; i++) {...}

// --- after ---
for(let i = arr.length; i--;) {...} // 注意 i-- 后面的分号别漏了
1
2
3
4
5
  • 8、null >= 0为true
  • 9、''>=0为true

# 只用这 6 个字符,就可以写出任意 JavaScript 代码

6 个字符

[]()!+

# 6 个字符的原理

我们之所以能够抛开其他字符不用,要归功于 JavaScript 的类型系统和数据类型转换机制。这 6 个字符是这样各显神通的:[]可以用来创建数组!和+可以在数组上执行一些操作再用()给这些操作分组

先看一个简单的数组:

[]
1

数组前加上!会把它转成布尔值。数组被认为是真值,因此取非之后变成了false:

![] === false
1

除非转换为类似类型,否则无法将不同类型的值加在一起。JavaScript 在进行转换时遵循一个预定义的规则:

  • 在表达式2 + true中,JavaScript 会将true转成数字,得到表达式2+1
  • 在表达式2 + "2"中,JavaScript 会将数字转成字符串,得到2 + "2" === "22"

WARNING

这些转换规则还不算糟糕,但是对于其他类型,好戏马上来了。

# JavaScript 数组强制转换

数组相加会转换成字符串并连接起来。空数组转换为空字符串,因此将两个数组相加将得到空字符串。

[] + [] === "" + "" === ""
1

数组跟其他类型值相加时也一样:

![] + [] === "false" + "" === "false"
1

惊不惊喜?我们得到了目标字符串"self"所包含的几个字符!如果我们能产生一些数字,就可以按正确的顺序提取所需的字符:

"false"[3] === "s" 
(![] + [])[3] === "s"
1
2

# 生成数字

前面提到了,可以把数组转成布尔值。那如果用加号+把它转成数字会怎样?

+[] === ???
1

JavaScript 会尝试调用数组的valueOf方法,但是发现不存在这个方法,然后就转而调用toString()方法了。因此上面的代码等效于:

+[] === +""
1

将字符串转换为数字将产生以下结果:

+"42" === 42
+"esg" == NaN
+"" === 0
1
2
3

空字符串是一个 false值,跟 null,undefined和数字零类似,因此将其中任何一个转换为数字都会变成零:

+null === 0
+undefined === 0
+false === 0
+NaN === 0
+"" === 0
1
2
3
4
5

因此,将数组转换为数字需要先将其转换为字符串,最后转成 0:

+[] === +"" === 0
1

第一个数字已经造出来了!我们还需要更多数字,继续:

!0 === !false
!false === true
!0 === true
1
2
3

将 0 取否就得到一个为真的布尔值。为真的布尔值转成数字,就是1:

+true === 1
1

有了 1,自然就可以得到2,所谓道生一,一生二,二生三,三生万物……

用上面的转换大法,可以轻松得到我们想要的这些数字:

1 === +true == +(!0) ==== +(!(+[])) === +!+[]1 === +!+[]2 === +!+[] +!+[]3 === +!+[] +!+[] +!+[]4 === +!+[] +!+[] +!+[] +!+[]
1

# 临门一脚,大功告成

总结下这些规则:

  • 数组属于真值,取否就得到 false: ![] // false
  • 数组相加时会转换成字符:[] + [] // ""
  • 空数组转成数字得到 0,再去否得到 true,再转成数字得到1:+(!(+[])) === 1

根据这些规则,我们就能得到想要的字符串。看下面这个示意图就很清楚了:

![] + [] === "false"+!+[] === 1(![] + [])[3] + (![] + [])[4] + (![] + [])[2] + (![] + [])[0]^^^^^^^^^^      ^^^^^^^^^^      ^^^^^^^^^^      ^^^^^^^^^^        "false"         "false"         "false"         "false"       ^^^^^^^^^^^^^   ^^^^^^^^^^^^^   ^^^^^^^^^^^^^   ^^^^^^^^^^^^^          s               e               l               f
1

最终的表达式就是这样:

(![] + [])[+!+[]+!+[]+!+[]] + (![] + [])[+!+[]+!+[]+!+[]+!+[]] + (![] + [])[+!+[]+!+[]] +(![] + [])[+[]]
1

整理下空格和换行,就是一行代码:

(![]+[])[+!+[]+!+[]+!+[]]+(![]+[])[+!+[]+!+[]+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]+(![]+[])[+[]]
1

# 骂人彩蛋

(!(~+[])+{})[--[~+""][+[]]*[~+[]]+~~!+[]]+({}+[])[[~!+[]]*~+[]];//sb

([][[]] + [])[+!![]] + ([] + {})[!+[] + !![]];//nb
1
2
3

# 默认值策略

但是默认值生效的前提是:ES6内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格等于undefined,默认值才会生效

let [x, y = 'b'] = ['a']; // x='a', y='b'

let [z = 1] = [undefined];
console.log(z); // 1

let [k = 1] = [null];
console.log(k); // null
1
2
3
4
5
6
7
  • 对象也可以使用默认值,但是前提是:对象的属性值严格等于undefined
var {x = 3} = {x: undefined};
console.log(x); // 3

var {y = 3} = {y: null};
console.log(y); // null
1
2
3
4
5

# 20个古怪表达式,并要猜出其输出结果

true + false//1

**1.**//?????啥玩意

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

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

10,2//2

!!""//false

+!![]//1

true == "true"//false

010 - 03//5(一个数字以0开头,那么在JavaScript中它就被当作一个八进制数字)
// 0b开头,那么它在JavaScript中被视为二进制数字。
// 0x开头,它在JavaScript中被当作一个十六进制数字。

''- -''//0
// -'' 为 0
// '' 为0

null + 0//0

0/0//NaN

1/0 === 10 ** 1000//true,
// 当除数不是0时,JavaScript认为这个表达式的结果是Infinity
// 10**1000是一个很大数字,JS 无法正确表示这个数字。(JavaScript中最高的整数值是2^53-1)。所以10 * 1000也被当作无限大(Infinity)。

true++//错误语法

"" - 1//-1

(null - 1) - "1"//-2

38 * 4343 * 2342+ (true” — 0)// NaN
// 在JavaScript的数字运算中,只要有一个值是NaN

5 + !5 + !!5//6

[] + [1] + 2//12

1 + 2 + "3"// 33
// JavaScript 从左到右执行这些操作。当数字3与字符串3相加时,字符串连接将优先进行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47