20200325

@xiaojingzhao

Plan

  • 你不知道的 js 中卷 -- 完结

Notes

性能测试与调优

  1. 如果单纯的使用 Date 来计算是不一定准确的,还和计时器的精度有关(这部分没有看懂是怎么计算的)

    重复一个 100 次的运算,总共消耗了 137ms,那么平均用时是 1.37ms 嘛?并不完全是。如果有异常值会导致误差

    可以循环运算以达到某个固定时间,而这个固定时间应该根据使用的定时器(不明白这个定时器是什么?是 setTimeout 嘛?)的精度而定。 15ms 精度的定时器需要迭代运行到 750ms 才能最小化不确定性到 1%。1ms 的定时器需要运行到 50ms 就可以达到不确定性小于 1%

    不是很明白以上的计算结论?

  2. 推荐使用 Benchmark.js

  3. 测试需要考虑各种测试环境 -- 推荐 jsPerf

  4. 不要过分的纠结微性能 -- 编译器会做一定的优化

  5. js 的引擎各有不同

    针对 v8 内部细节的实现,有人总结了一下两个细节来提高代码的性能。但是不必纠结于这两条规则,万一以后不是 v8 了呢?

    a) 不要把 arguments 从一个函数传到另一个函数,会降低函数实现速度 b) 把 trycatch 分离到单独的函数中。因为浏览器在优化 trycatch 时会有一些困难

尾调用优化

尾调用就是出现在另一个函数结尾处的函数调用。

function foo(x) {
  return x;
}

function bar(y) {
  return foo(y + 1); // 尾调用
}

function baz() {
  return 1 + bar(40); // 非尾调用
}

优点: 每次调用一个新的函数都需要额外的一块预留内存来管理调用栈 -- 栈帧。以上面的例子为例:需要为每个 foo, bar, baz 保留一个栈帧。如果引擎能够识别到 foo 是尾调用,那么就可以认为 bar 基本完成了,所以可以重复利用 bar 的栈帧。这就是为什么尾调用可以提高性能

递归尾调用

function factorial(n) {
  function fact(n, res) {
    if (n < 2) return res;
    return fact(n - 1, n * res); // 尾调用
  }
  return fact(n, 1); // 尾调用
}

More