《高性能JavaScript》笔记

用一周的时间看完了《高性能Javascript》,涉及到了javascript很多个方面的性能问题,但由于书比较薄,基本点到即止,但还是刷新了我之前的一些对javascript的误解甚至误用,还让我了解到了javscript的一些冷知识和独到的技巧。读书过程对其中一部分做了笔记,但没有全部,有些没有在电脑前看就没有记录下来了。

JS 从循环性能到 Duff's Device

JS的循环种类和性能:

1、标准for循环

for (var i = 0, len = list.length; i < len; i++) {
  // todo...
}

2、while循环
又分前侧循环和后测循环:

while(i--) {
  // todo ...
}

do {
 // todo ...
} while(i--)

3、for-in 循环

for (var i in obj) {
 // todo 
}

前两种循环的性能基本一样,唯独第三种循环的性能明显比前两种低,问题在于每次迭代操作都会搜索实例或者实例属性, 速度大约为其他两种的1/7,除非你需要迭代属性未知的对象,否则尽量不要使用这种方法去迭代。

宁可罗列出该对象的属性,然后用for循环去遍历该对象:

var props = ['props1', 'props2']
  , i
  ;
for (i = 0; i < props.length; i++) {
  process(obj[props[i]]);
}

减少迭代中的操作次数
每次迭代,减少对遍历对象的属性读取如长度,对于重复的存为局部变量,减少初始化的变量,这些应该很好理解:

var  i = arr.length;
for (; i > 0;i--) {
  // todo
|

** 再深一步:减少迭代的次数 **
Duff's Device 是一种循环体展开技术,它让每一次迭代执行了多次迭代的操作,从而减少了迭代的次数。

var iterations = Math.floor(items.length / 8)
  , start_at = item.length % 8
  , i = 0
  ;

do {
  switch(start_at ) {
    case 0: process(items[i++]);
    case 7: process(items[i++]);
    case 6: process(items[i++]);
    case 5: process(items[i++]);
    case 4: process(items[i++]);
    case 3: process(items[i++]);
    case 2: process(items[i++]);
    case 1: process(items[i++]);
  }
  start_at = 0;
} while(iterations--);

如果迭代次数超过1000,执行效率会提高70%左右,笔者试验了结果,发现无论是否是使用了这种技术,效率上并没有所谓的提升,Demo

** 优化这种技术 **

var leftover = items.length % 8
  , i = 0
  ;
while(leftover--) {
  process(items[i++]);
}

var iterations = Math.floor(items.length / 8);

while(iterations--) {
  process(items[i++]);
}

** ECMA第四版提供了一个新的原生数组方法forEach(), **
尽管该方法能很便利地去遍历每个成员,但是它仍然比基于循环的迭代要慢一些,对每一数组项调用外部方法所带来的开销是速度慢的主要原因

递归与调用栈

** 调用栈的限制引出安全问题 **
Javascript引擎支持的递归数量与Javascript调用栈大小直接相关, 只有IE例外,它的调用栈和系统空闲内存有关,其他浏览器都有固定数量的调用栈闲置。
递归可能是自身调用的递归,可能是相互的递归,如A调用B, B调用A,这种情况如果出错很难找到问题,为了安全,尽可能地使用迭代的方式。
** 迭代来解决安全问题 **
使用优化后的迭代替代长时间运行的递归函数可以提升性能,因为运行一个循环比反复调用一个函数的开销要少得多。

** 使用内存缓存来减少递归的调用 **

function menfactorial(n) {
  if (!menfactorial.cache) {
    menfactorial.cache = {
      '0': 1,
      '1': 1
    }
  }
  if (!menfactorial.cache.hasOwnProperty(n)) {
    menfactorial.cache[n] = n * menfactorial(n-1)
  }
  return menfactorial.cache[n];
}

字符串和正则表达式

** 使用数组连接字符串更快?**
这句话对又不对,之所以会出现使用数组连接字符串,是由于傻逼的IE7的连接算法要求浏览器在循环过程中为逐渐增大的字符串他不断复制并分配内存,结果是运行时间和内存消耗以平方关系递增,于是当我们使用数组去连接时,由于一次分配够了内存,性能就得到了很大的提升。

然而对于IE8后和其他浏览器,数组连接和普通的字符串连接性能差异其实不大,详情可以看DEMO

** concat **
String.prototype.concat 又怎么样呢?比使用简单的+和+=稍慢,比join也慢,所以不建议使用。

** 慎用正则表达式 **
从编译、设置起始位置、匹配每个正则表达式到匹配成功与失败

正则中的回溯是影响其速度的重要部分

trim的实现:

if (!String.prototype.trim) {
  String.prototype.trim = function() {
    return this.replace(/^\s+/, '').replace(/\s+$/, '');
  }
}

快速响应的用户界面

** 100毫秒准则 **
如果界面在超过100毫秒响应用户的输入,那么用户会感觉自己与界面失去了联系,由于js运行时无法更新UI,所以如果javascript的运行超过100毫秒,用户会感觉失去了对界面的控制

除了脚本运行的时间,如果网站采用了异步脚本加载的技术,那么还要考虑脚本加载过来的时间,这个时候采用预加载的方式可以减少由于脚本加载而带来的延迟,比如但用户点击某个按钮触发脚本加载时,我们可以改为但用户把鼠标放在按钮上时就去加载脚本,这样,等到用户去点击的时候,脚本就已经加载好了。

** 定时器 **
当脚本运行时间超过100毫秒,需要想办法分割执行代码段,从而让界面能够响应用户的交互:

function processArray(items, process, callback) {
  var todo = items.concat();
  setTimeout(function() {
    process(todo.shift());
    if (todo..length > 0) {
      setTimeout(arguments.callee, 25);8
    } else {
      callback(items);  
    }
  }, 25);
}

使用定时器对循环进行分割是一种方法,请看demo,同理,你也可以对要执行的函数队列进行分割执行,同时记录每个函数的运行时间。

注意一个页面最好只有一个定时器,一方面我们可以更好地控制时间队列,防止多个定时器抢占时间队列,另一方面防止过多的时间器导致时间分割过小反而引起性能的问题。

** Web Worker **
新版浏览器支持的特性,允许在UI线程外执行javascript代码,从而避免锁定UI

Ajax

五中常用技术用于向服务器请求数据

  • XML
  • Dynamic script tag insertion
  • iframes
  • Comet
  • Multipart XHR

编程过程的性能消耗

** 昂贵的双重求值 **
js和其他脚本语言一样,允许我们在程序中提取一个包含代码的字符串,然后动态执行它,可以通过eval、function构造函数和两个定时器

var num1 = 1, num2 = 2
  ;

var sum = eval('num1 + num2');

var sum2 = new Function('arg1', 'arg2', 'return arg1 + arg2');

setTimeout('sum = num1 + sum2', 1000);

setInterval('sum = num1 + sum2', 1000);

字符串代码会以正常方式求值,然后再对里面的代码进行求值,代价昂贵。

** 直接使用Object/ Array直接量 ** 
尽管我们可以使用像其他语言的方法一样先实例化构造函数,然后再赋值:
var obj = new Object();
obj.name = 'cjs';
obj.count = 30;

当使用直接量更加快,不仅代码量小了,运行更加快
var obj = {
  name: 'cjs',
  count: 30
}

** 使用延迟加载和预加载 **
前端经常要兼容各个浏览器,导致了对于一个功能的函数,往往需要判断用户所在的浏览器再执行不同的功能,我们可以使用延迟加载或者预加载的方式来防止每次执行时的判断

** 位运算 **
基于底层操作的位运算是javascript运行最快的部分之一,使用的好可以提升一倍以上的速度,这是为什么很多图形处理的代码都使用位运算符进行计算的原因之一。
使用与运算符 & 可以用来替代判断奇数和偶数,使用或运算符可以用来组合数字,还有按位左移(<< )和按位右移(>>)和无符号右移(<<)等运算符

** 原生方法 **
javascript的原生部分都是用低级语言写的,如C++, 意味着这些方法已经被编译为机器码,成为浏览器的一部分,所以永远比我们自己写的代码要快。
如Math对象提供的方法、css选择器API等,使用原生的querySelector查询所用的时间是jq查询的10%

### 构建部署高性能的javascript应用
前端构建工具提供了代码文件的合并、压缩,HTML5的离线应用缓存等概念
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容

  • 最近在阅读这本Nicholas C.Zakas(javascript高级程序设计作者)写的最佳实践、性能优化类的书...
    undefinedR阅读 2,152评论 0 30
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,047评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,494评论 18 139
  • 昨夜元宵尽,今日已踏回。如昔又一新,劈浪勇前行。随老就耕父,成长伴幼童。艳阳占气候,共说此年丰。
    八斗才001阅读 264评论 0 1
  • 这是一本书 有这样一本书,在我因为生活中的挫折而伤心低沉的时候,它告诉我:没有人能够永远快乐,见识的少,所...
    星辰不及你眉眼阅读 290评论 0 0