文档章节

编写高性能的 JavaScript:

duozuo2006
 duozuo2006
发布于 2016/12/06 01:02
字数 1318
阅读 3
收藏 0

本文的初衷是想介绍如何利用些简单的代码小技巧就能促进JavaScript编译器的优化进程从而提升代码运行效率。特别是在游戏这种对于垃圾回收速度要求较高,你性能稍微差点用户就能见到白屏的地方。

Monomorphism:单态性

JavaScript中允许函数调用时候传入动态参数,不过就以简单的2参数函数为例,当你的参数类型、参数数目与返回类型动态调用时才能决定,编译器需要更多的时间来解析。编译器自然地希望能够处理那些单态可预测的数据结构、参数统计等。

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:常量

使用常量能够让编译器在编译时即完成变量的值替换:

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编译器能够找出你的代码中被执行次数最多的部分,将你的代码分割成多个小的代码块能够有助于编译器在编译时将这些代码块转化为内联格式然后增加执行速度。

Data Types:数据类型

尽可能地多用Numbers与Booleans类型,因为他们与其他类似于字符串等原始类型相比性能表现更好。使用字符串类型可能会带来额外的垃圾回收消耗。

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

尽可能使用===这个严格比较操作符而不是==操作符。使用严格比较操作符能够避免编译器进行类型推导与转换,从而提高一定的性能。

Strict Conditions

JavaScript中的if语句也非常灵活,你可以直接在if(a) then bla这个类型的条件选择语句中传入随意类似的a值。不过这种情况下,就像上文提及的严格比较操作符与宽松比较操作符一样,编译器需要将其转化为多个数据类型进行比较,而不能立刻得出结果。当然,这并不是一味的反对使用简写方式,而是在非常强调性能的场景,还是建议做好每一个细节的优化:

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

);

© 著作权归作者所有

共有 人打赏支持
duozuo2006
粉丝 0
博文 13
码字总数 8728
作品 0
北京
编写高性能的 JavaScript 程序的几个提示

这是一篇来自国外的文章,从各个方面介绍如何编写一个高性能的 JavaScript 应用程序。例如应该在页面最底部加载JS文件、合并多个js文件、异步加载js文件等等。 全文阅读(英文)...

鉴客
2011/03/30
0
0
十款最新的JavaScript开发工具

Kreate 这款工具有助于对 jQuery对象快速生成DOM。你可以创建单个或多个元素。 2. Night Watch js Nightwatchjs拥有一个简洁而强大的语法,能够允许你快速编写测试。它是专用于浏览器应用和网...

zuiw
2014/05/29
0
1
开发人员常用的10个JavaScript库

最近有很多学员在问我,有没有常用的javascript库,他们在日常使用的时候不知道去哪儿找。我在此整理出来,供麦子学院的学员们参考。 MooTools MooTools是一个简洁,模块化,面向对象的JavaS...

yexiaobo1990
2015/07/24
0
0
【译】为什么ReasonReact是编写React的最佳方式

原文地址:medium.freecodecamp.org/psst-heres-… 你是否使用 React 来构建用户界面?好吧,我也是。接下来你会了解为什么要使用 ReasonML 来写 React 应用。 React 是构建用户界面的一种很...

吃吃吃吃不胖
10/18
0
0
零基础的网站开发初学者如何系统的学习?

A.学习背景 开始网站开发,起先是从手机网站的UBB语法开始的,也因此有了深入学习的兴趣。为了让学习的热情膨胀,我有意的培养这一爱好 ,定位了自己要成为一名优秀的网站开发员的目标。 作为...

李佳顺
2012/12/25
0
3

没有更多内容

加载失败,请刷新页面

加载更多

linux 系统的运行级别

运行级别 运行级别 | 含义 0 关机 1 单用户模式,可以想象为windows 的安全模式,主要用于修复系统 2 不完全的命令模式,不含NFS服务 3 完全的命令行模式,就是标准的字符界面 4 系统保留 5 ...

Linux学习笔记
今天
2
0
学习设计模式——命令模式

任何模式的出现,都是为了解决一些特定的场景的耦合问题,以达到对修改封闭,对扩展开放的效果。命令模式也不例外: 命令模式是为了解决命令的请求者和命令的实现者之间的耦合关系。 解决了这...

江左煤郎
今天
3
0
字典树收集(非线程安全,后续做线程安全改进)

将500W个单词放进一个数据结构进行存储,然后进行快速比对,判断一个单词是不是这个500W单词之中的;来了一个单词前缀,给出500w个单词中有多少个单词是该前缀. 1、这个需求首先需要设计好数据结...

算法之名
昨天
14
0
GRASP设计模式

此文参考了这篇博客,建议读者阅读原文。 面向对象(Object-Oriented,OO)是当下软件开发的主流方法。在OO分析与设计中,我们首先从问题领域中抽象出领域模型,在领域模型中以适当的粒度归纳...

克虏伯
昨天
1
0
Coding and Paper Letter(四十)

资源整理。 1 Coding: 1.Tomislav Hengl撰写的非官方作者指南:Michael Gould•Wouter Gerritsma。 UnofficialGuide4Authors 2.R语言包rwrfhydro,社区贡献的工具箱,用于管理,分析和可视化...

胖胖雕
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部