Live Note

Remain optimistic

属性名表达式

JavaScript 定义属性有两种方法:

1
2
obj.foo = true
obj["a" + "bc"] = 123

ES6 允许字面量定义对象时使用第二种方法:

1
2
3
4
5
6
7
8
let propKey = "foo"
let obj = {
[propKey]: true,
["a" + "bc"]: 123,
["he" + "llo"]() {
return "hi"
},
}
Read more »

扩展运算符

1
2
3
4
5
6
7
8
9
10
11
console.log(...[1, 2, 3]) //1 2 3
console.log(1, ...[2, 3], 4) //1 2 3 4
;[...document.querySelectorAll("div")] //[<div>, <div>, ...];

function add(x, y) {
return x + y
}
add(...[1, 2]) //3

//与表达式一同使用
const arr = [...(x > 0 ? ["a"] : []), "b"]

扩展运算符的应用

  • 替代数组的 apply 方法

    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
    function f(x, y, z) {}
    var args = [0, 1, 2]

    //ES5 写法
    f.apply(null, args)

    //ES6 写法
    f(...args)

    //用 Math.max 求数组中最大的数
    //ES5 写法
    Math.max.apply(null, [2, 33, 3])

    //ES6 写法
    Math.max(...[2, 33, 3])

    //用 push 将一个数组添加到另一个数组尾部
    var arr1 = [0, 1, 2]
    var arr2 = [3, 4, 5]

    //ES5 写法
    Array.prototype.push.apply(arr1, arr2)

    //ES6 写法
    arr1.push(...arr2)
  • 合并数组

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var arr1 = ["a"]
    var arr2 = ["b", "c"]
    var arr3 = ["d"]

    //ES5 写法
    arr1.concat(arr2, arr3)

    //ES6 写法
    ;[...arr1, ...arr2, ...arr3]
  • 与解构赋值结合

    Read more »

尾调用

Tail Call 是函数式编程的一个重要概念,就是指某个函数的最后一步是调用另一个函数。

1
2
3
function f(x) {
return g(x)
}

尾调用不一定出现在函数尾部,只要是最后一步操作即可。

1
2
3
4
function f(x) {
if (x > 0) return t(x)
return m(x)
}

尾调用优化

函数调用会在内存中形成一个 call frame,保存调用位置和内部变量等信息。所有的 call frame 形成一个 call stack。
尾调用由于是函数的最后一步操作,所以不需要保留外层函数的 call frame,这就叫 Tail Call Optimization,即只保留内层函数的调用帧。

尾递归

递归非常耗内存,因为需要同时保存多个 call frame,很容易发生 stack overflow。但对于尾递归来说,由于只存在一个 call frame,所以不会发生溢出。

1
2
3
4
5
6
7
function Fibonacci(n) {
if (n <= 1) return 1
return Fibonacci(n - 1) + Fibonacci(n - 2)
}
console.log(Fibonacci(10)) //89
console.log(Fibonacci(100)) //overflow
console.log(Fibonacci(500)) //overflow
Read more »

函数的默认值

在 ES6 之前,不能直接为函数指定默认值,只能采用变通的方法:

1
2
3
4
5
6
7
8
function log(x, y) {
if (y === "undefined") {
y = "world"
}
console.log(x, y)
}
log("hello") //hello test
log("hello", "") //hello

ES6 允许为函数的参数设置默认值:

1
2
3
4
5
6
7
8
9
10
11
12
function log(x, y = "world") {
console.log(x, y)
}
log("hello") //hello world
log("hello", "") //hello

eg: function Point(x = 0, y = 0) {
this.x = x
this.y = y
}
let p = new Point()
console.log(p.x + " " + p.y) //0 0
Read more »

~运算符

位运算符是三步处理的过程:

  1. 把运算数转换成 32 位数字
  2. 把二进制数转换成它的二进制反码
  3. 把二进制数转换成浮点数
1
2
3
var iNum1 = 25 //25 等于 00000000000000000000000000011001
var iNum2 = ~iNum1 //转换为 11111111111111111111111111100110
alert(iNum2) //输出 "-26"

~~技巧

这是刷题时看见别人的代码里使用的东西,通常用来代替 Math.trunc()的方法。

1
2
3
4
5
6
//单个 ~
console.log(~1337) // -1338
//数字输入
console.log(~~47.11) //47
console.log(~~1.9999) //1
console.log(~~3) //3

当原始输入不确定时,~~可以将任何非数字类型转换成 0:

1
2
3
4
5
6
7
8
console.log(~~[]) //0
console.log(~~NaN) //0
console.log(~~null) //0

// | 0 也是相同的效果
console.log([] | 0) //0
console.log(NaN | 0) //0
console.log(null | 0) //0
Read more »

Math.trunc()

Math.trunc 用于除去一个数的小数部分。

1
2
3
4
5
6
7
8
9
10
11
Math.trunc(111.22) //111
Math.trunc("123.456") //123
//对空和无法截取整数的值,返回 NaN
Math.trunc(NaN) //NaN

//代码模拟
Math.trunc =
Math.trunc ||
function (x) {
return x < 0 ? Math.ceil(x) : Math.floor(x)
}
Read more »

进制表示

0b(0B)表示二进制,0o(0O)表示八进制

Number.isFinite()、Number.isNaN()

Number.isFinite 用来检查一个数值是否为有限的。

1
2
3
4
5
6
Number.isFinite(NaN) //false
Number.isFinite(Infinity) //false
Number.isFinite(-Infinity) //false
Number.isFinite("15") //false
Number.isFinite("foo") //false
Number.isFinite(true) //false
Read more »

基本用法

1
2
3
4
5
var regex = new RegExp("xyz", "i")
var regex = new RegExp(/xyz/i)
var regex = /xyz/i

new RegExp(/abc/gi, "i").flags //ig 被覆盖成 i

match()、replace()、search()、split()

String.prototype.match 调用 RegExp.prototype[Symbol.match]
String.prototype.replace 调用 RegExp.prototype[Symbol.replace]
String.prototype.search 调用 RegExp.prototype[Symbol.search]
String.prototype.split 调用 RegExp.prototype[Symbol.split]

Read more »

codePointAt()

JavaScript-16 的格式储存,每个字符固定为两个字节。需要四个字节储存的字符,JavaScript 会认为那是两个字符。
ES6 提供了 codePointAt 方法,能够正确处理四个字节储存的字符,返回一个字符的码点。

String.fromCodePoint()

识别 UTF-32 字符。

includes()、startsWith()、endsWith()

1
2
3
4
5
6
7
var s = "hello world"
s.startWith("hello") //true
s.endsWith("d") //true
s.includes("o") //true

s.startsWith("world", 6) //true;
s.includes("hello", 6) //false

repeat()

1
2
3
4
5
6
7
8
9
10
11
"x".repeat(3) //'xxx'
"na".repeat(0) //''

"na".repeat(2.9) //取整 -> 'nana'

"na".repeat(Infinity) //Error
"na".repeat(-1) //Error

"na".repeat(-0.9) //''
"na".repeat(NaN) //''
"na".repeat("3") //'nanana'
Read more »

基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let [a, b, c] = [1, 2, 3]
let [foo, [[bar], baz]] = [1, [[2], 3]]

let [, , third] = ["foo", "bar", "baz"]
third //'baz'
let [x, , y] = [1, 2, 3]
x, y //1, 3

let [head, ...tail] = [1, 2, 3, 4]
head //1
tail //[2, 3, 4]

let [x, y, ...z] = ["a"]
x //a
y //undefined
z //[]

let [x, y, z] = new Set(["a", "b", "c"])
x //'a'
Read more »