2016:編寫高性能的JavaScript

2016:編寫高性能的JavaScript翻譯自Felix Maier的文章Writing-Efficient-JavaScript。本文從屬於筆者的Web 前端入門與最佳實踐JavaScript 入門與最佳實踐系列文章。javascript

本文的初衷是想介紹如何利用些簡單的代碼小技巧就能促進JavaScript編譯器的優化進程從而提高代碼運行效率。特別是在遊戲這種對於垃圾回收速度要求較高,你性能稍微差點用戶就能見到白屏的地方。前端

Monomorphism:單態性

JavaScript中容許函數調用時候傳入動態參數,不過就以簡單的2參數函數爲例,當你的參數類型、參數數目與返回類型動態調用時才能決定,編譯器須要更多的時間來解析。編譯器天然地但願可以處理那些單態可預測的數據結構、參數統計等。java

function example(a, b) {
  // we expect a, b to be numeric
  console.log(++a * ++b);
};

example(); // bad
example(1); // still bad
example("1", 2); // dammit meg

example(1, 2); // good

Constants:常量

使用常量可以讓編譯器在編譯時即完成變量的值替換:git

const a = 42; // we can easily unfold this
const b = 1337 * 2; // we can resolve this expression
const c = a + b; // still can be resolved
const d = Math.random() * c; // we can only unfold 'c'

// before unfolding
a;
b;
c;
d;

// after unfolding
// we can do this at compile time!
42;
2674;
2716;
Math.random() * 2716;

Inlining:內聯

JIT編譯器可以找出你的代碼中被執行次數最多的部分,將你的代碼分割成多個小的代碼塊可以有助於編譯器在編譯時將這些代碼塊轉化爲內聯格式而後增長執行速度。github

Data Types:數據類型

儘量地多用Numbers與Booleans類型,由於他們與其餘相似於字符串等原始類型相比性能表現更好。使用字符串類型可能會帶來額外的垃圾回收消耗。web

const ROBOT = 0;
const HUMAN = 1;
const SPIDER = 2;

let E_TYPE = {
  Robot: ROBOT,
  Human: HUMAN,
  Spider: SPIDER
};

// bad
// avoid uncached strings in heavy tasks (or better in general)
if (entity.type === "Robot") {
  
}

// good
// the compiler can resolve member expressions
// without much deepness pretty fast
if (entity.type === E_TYPE.Robot) {
  
}

// perfect
// right side of binary expression can even get unfold
if (entity.type === ROBOT) {
  
}

Strict & Abstract Operators

儘量使用===這個嚴格比較操做符而不是==操做符。使用嚴格比較操做符可以避免編譯器進行類型推導與轉換,從而提升必定的性能。express

Strict Conditions

JavaScript中的if語句也很是靈活,你能夠直接在if(a) then bla這個類型的條件選擇語句中傳入隨意相似的a值。不過這種狀況下,就像上文說起的嚴格比較操做符與寬鬆比較操做符同樣,編譯器須要將其轉化爲多個數據類型進行比較,而不能馬上得出結果。固然,這並非一味的反對使用簡寫方式,而是在很是強調性能的場景,仍是建議作好每個細節的優化:segmentfault

let a = 2;

// bad
// abstracts to check in the worst case:
// - is value equal to true
// - is value greater than zero
// - is value not null
// - is value not NaN
// ..
if (a) {
 // if a is true, do something 
}

// good
if (a === 2) {
  // do sth 
}

// same goes for functions
function b() {
  return (!false);
};

if (b()) {
  // get in here slow
}

if (b() === true) {
  // get in here fast
  // the compiler knows a specific value to compare with
}

Arguments

儘量避免使用arguments[index]方式進行參數獲取,而且儘可能避免修改傳入的參數變量:數組

function mul(a, b) {
  return (arguments[0]*arguments[1]); // bad, very slow
  return (a*b); // good
};

function test(a, b) {
  a = 5; // bad, dont modify argument identifiers
  let tmp = a; // good
  tmp *= 2; // we can now modify our fake 'a'
};

Toxicity:這些關鍵字有毒

Toxicity

以下列舉的幾個語法特性會影響優化進程:緩存

  • eval

  • with

  • try/catch

同時儘可能避免在函數內聲明函數或者閉包,可能在大量的運算中致使過多的垃圾回收操做。

Objecs

Object實例一般會共享隱類,所以當咱們訪問或者設置某個實例的未預約義變量值的時候會建立一個隱類。

// our hidden class 'hc_0'
class Vector {
  constructor(x, y) {
    // compiler finds and expects member declarations here
    this.x = x;
    this.y = y;
  }
};

// both vector objects share hidden class 'hc_0'
let vec1 = new Vector(0, 0);
let vec2 = new Vector(2, 2);

// bad, vec2 got hidden class 'hc_1' now
vec2.z = 0;

// good, compiler knows this member
vec2.x = 1;

Loops

儘量的緩存數組長度的計算值,而且儘量在同一個數組中存放單個類型。避免使用for-in語法來遍歷某個數組,由於它真的很慢。另外,continue與break語句在循環中的性能也是不錯的,這一點使用的時候不用擔憂。另外,儘量將短小的邏輯部分拆分到獨立的函數中,這樣更有利於編譯器進行優化。另外,使用前綴自增表達式,也能帶來小小的性能提高。(++i代替i++)

let badarray = [1, true, 0]; // bad, dont mix types
let array = [1, 0, 1]; // happy compiler

// bad choice
for (let key in array) {
  
};

// better
// but always try to cache the array size
let i = 0;
for (; i < array.length; ++i) {
  key = array[i];
};

// good
let i = 0;
let key = null;
let length = array.length;
for (; i < length; ++i) {
  key = array[i];
};

drawImage

draeImage函數算是最快的2D Canvas API之一了,不過咱們須要注意的是若是爲了圖方便省略了全參數傳入,也會增長性能損耗:

// bad
ctx.drawImage(
  img,
  x, y
);

// good
ctx.drawImage(
  img,
  // clipping
  sx, sy,
  sw, sh,
  // actual stuff
  x, y,
  w, h
);

// much hax
// no subpixel rendering by passing integers
ctx.drawImage(
  img,
  sx|0, sy|0,
  sw|0, sh|0,
  x|0, y|0,
  w|0, h|0
);
相關文章
相關標籤/搜索