循环

在javaScript中的四种循环中(for、for-in、while、do-while),只有for-in循环比其它几种明显要慢,另外三种速度区别不大

有一点需要注意的是,javascript没有块级作用域,只有函数级作用域,也就是说在for循环初始化中的var语句会创建一个函数级变量而非循环级变量

优化循环的方法有如下

1、减少对象成员及数组项的查找次数(使用局部变量保存需要查找的对象成员)

2、颠倒数组的顺序来提高循环性能,也就是从最后一项开始向前处理

        for (var i = arr.length-1; i >= 0 ; i--) {
//process
}

3、相信大家都会尽可能的使用for循环而非jQuery的each来遍历数组,那是因为jQuery的each方法是基于函数的迭代。尽管基于函数的迭代提供了一个更为便利的迭代方法,但它比基于循环的迭代在慢许多。

4、有时候我们会想到底是使用if-else呢还是使用switch,事实上在大多数情况下switch比if-else运行得要快,所以当判断多于两个离散值时,switch语句是更佳的选择

5、优化if-else最简单的方法就是确保最可能出现的条件放在首位,另外一个方法就是优化条件判断的次数,看下面的代码您就懂了

if (value == 0) {
return result0;
} else if (value == 1) {
return result1;
} else if (value == 2) {
return result2;
} else if (value == 3) {
return result3;
} else if (value == 4) {
return result4;
} else if (value == 5) {
return result5;
} else if (value == 6) {
return result6;
} else if (value == 7) {
return result7;
} else if (value == 8) {
return result8;
} else if (value == 9) {
return result9;
} else if (value == 10) {
return result10;
}

下面这种方法就是使用二分搜索法将值域分成一系列区间,然后逐步缩小区范围,对上面的例子进行的优化

            if (value < 6) {
if (value < 3) {
if (value == 0) {
return result0;
} else if (value == 1) {
return result1;
} else {
return result2;
}
} else {
if (value == 3) {
return result3;
} else if (value == 4) {
return result4;
} else {
return result5;
}
}
} else {
if (value < 8) {
if (value == 6) {
return result06;
} else if (value == 7) {
return result7;
}
} else {
if (value == 8) {
return result8;
} else if (value == 9) {
return result9;
} else {
return result10;
}
}
}

6、使用递归虽然可以把复杂的算法变得简单,但递归函数如果终止条件不明确或缺少终止条件会导致函数长时间运行。所以递归函数还可能会遇到浏览器“调用栈大小限制”

使用优化后的循环来替代长时间运行的递归函数可以提升性能,因为运行一个循环比反复调用一个函数的开销要少的多

如果循环资料太多,可以考虑使用如下介绍的达夫设备原理来提升性能

达夫设备

        var iterations = Math.floor(items.length / 8),
startAt = items.length % 8,
i = 0;
do {
//每次循环最多可调用8次process
switch (startAt) {
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++]);
}
startAt = 0;
} while (--iterations);
        var i = items.length % 8;
while (i) {
process(items[i--]);
}
i = Math.floor(items.length / 8);
while (i) {
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
}

Memoization

避免重复是Memoization的核心思想,它缓存前一次计算结果供后续使用,下面代码就是利用缓存结果的思想计算阶乘的

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

写成通用方法如下代码所示:

        function memoize(fundamental, cache) {
cache = cache || {};
var shell = function (arg) {
if (!cache.hasOwnProperty(arg)) {
cache[arg] = fundamental(arg);
}
return cache[arg];
}
return shell;
}
//下面是调用示例
function factorial(n) {
if (n==0) {
return 1;
}else{
return n*factorial(n-1);
}
}
var memfactorial = memoize(factorial, { "0": 1, "1": 1 });
memfactorial(6);

算法和流程控制小结

字符串优化

        str += "one" + "two";
//以下代码分别用两行语句直接附加内容给str,从而避免产生临时字符串 性能比上面提升10%到40%;
str += "one";
str += "two";
//同样你可以用如下一句达到上面同样的性能提升
str = str + "one" + "two";
//事实上 str = str + "one" + "two";等价于 str = ((str + "one") + "two");

或许大家都喜欢用Array.prototype.join方法将数组中所有元素合并成一个字符串,虽然它是在IE7及更早版本浏览器 中合并大量字符串唯一高效的途径,但是事实上在现代大多数浏览器中,数组项连接比其它字符串连接的方法更慢。

在大多数情况下,使用concat比使用简单的+和+=要稍慢些。

快速响应用户界面

javascript是单线程的,共用于执行javascript和更新用户界面的进程通常被称作为“浏览器UI线程”,也就是说某一时间,UI线程只能做一件事情,要么执行javascript,要么更新用户界面,假设你当前的javascript需要执行很长时间,而这时候用户点击了界面按钮,那么UI线程则无法立即响应用户点击更新按钮UI状态,导致用户以为没点击而进行多次点击操作

所以有些浏览器会限制javascript任务的运行时间

单个javascript操作花费的总时间不应该超过100毫秒,所以应该限制所有javascript任务在100毫秒或更短的时间内完成,但是。。。

应该让出UI控制权(也就是停止执行javascript)使得UI线程有机会更新,然后再继续执行javascript

使用setTimeout和setInterval来创建定时器(定时器代码只有在创建它的函数执行完成之后,才有可能被执行)

使用setTimeout和使用setInterval几乎相同,唯一的区别在于如果UI队列中已经存在由同一个setInterval创建的任务,那么后续任务不会被添加到UI队列中

每个定时器最好使用至少25毫秒,因为更小的延时对于大多数UI更新根本不够用

        function processArray(items,process,callback) {
var todo = items.concat();//克隆原数组
setTimeout(function () {
process(todo.shift());
if (todo.length > 0) {
setTimeout(arguments.callee, 25);
} else {
callback(items);
}
}, 25);
}
//for example
var items = [123, 45, 443, 35, 53, 7544, 7654, 75, 75, 32, 653, 76];
function outputValue(value) {
console.log(value);
}
processArray(items, outputValue, function () {
console.log("Done");
});

同理,分割任务也是一样。

        function multiStep(steps,args,callback) {
var tasks = steps.concat();//克隆数组
setTimeout(function () {
var task = tasks.shift();//执行下一个任务
task.apply(null, args || []);
//检查是否还有其它任务
if (tasks.length > 0) {
setTimeout(arguments.callee, 25);
} else {
callback();
}
}, 25);
}
//for example
var tasks = [openDoc, writeText, closeDoc, updateUI];//由待执行函数组成的数组
multiStep(tasks, [id], function () {
console.log("Done");
});

相信了解过Html5的都知道,h5中引用了web worker来执行与ui更新无关的长脚本,这个也可以改善用户响应时间。具体请见我的另外博文

web worker

快速响应的用户界面小结

数据传输

有5种常用技术用于向服务器请求数据:

  1. XMLHttpRquest(XHR)
  2. Dynamic script tag insertion动态脚本注入
  3. iframes
  4. Commet
  5. Multipart XHR

XMLHttpRquest:允许异步发送和接收数据,可以在请求中添加任何头信息和参数,并读取服务器返回的所有头信息及响应文本

使用XHR时,POST和GET的对比,对于那些不会改变服务器状态,只会获取数据(这种称作幂等行为)的请求,应该使用GET,经GET请求的数据会被缓存起来,如果需要多次请求同一数据的时候它会有助于提升性能 。

只有当请求的URL加上参数的长度接近或超过2048个字符时,才应该用POST获取数据,这是因为IE限制URL长度,过长时将会导致请求的URL截断

另外需要注意的是:因为响应消息作为脚本标签的源码,所以返回的数据必须是可执行的javascript代码,所以你不能使用纯xml,纯json或其它任何格式的数据,无论哪种格式,都必须封装在一个回调函数中

使用XHR发送数据到服务器时,GET方式会更快,因为对于少量数据而言,一个GET请求往服务器只发送一个数据包,而一个POST请求至少发送两个数据包,一个装载头信息,另一个装载POST正文,POST更适合发送大量数据到服务器

Multipart XHR:允许客户端只用一个HTTP请求就可以从服务器向客户羰传送多个资源,它通过在服务器端将资源打包成一个由双方约定的字符串分割的长字符串并发送到客户端,然后用javaScript处理那个长字符串,并根据mime-type类型和传入的其它头信息解析出每个资源

multipart XHR使用了流的功能,通过监听readyState为3的状态,我们可以在一个较大的响应还没有完全接受之前就把它分段处理,这样我们就可以实时处理响应片段,这也是MXHR能大幅提升性能的主要原因

使用Multipart XHR的缺点(但是它能显著提升页面的整体性能):

  1. 获得的资源不能被浏览器缓存
  2. 老版本的IE不支持readyState为3的状态和data:URL(图片不是由base64字符串转换成二进制,而是使用data:URL的方式创建,并指定mime-type为image/jpeg    使用readyState为3是因为你不可能等所有数据都传输完成再处理,那样会很慢)

Beacons技术

使用javascript创建一个新的Image对象,并把src属性设置为服务器上脚本的URL,该URL包含我们要通过GET传回的键值对数据(并没有创建img元素,也没有插入DOM),服务器会接收到数据并保存起来,它需向客户端发送任何回馈信息。这种方式是给服务器回传信息最有效的方式,虽然它的优点是性能消耗很小,但它的缺点也显而易见

发送的数据长度限制得相当小

如果要接收服务器端返回的数据一种方式是监听Image对象的load事件,另外一种方式就是检查服务器返回图片的宽高来判断服务器状态

数据格式

现在xml这种数据格式已全然被json取代了,原因很多,主要原因是XML文件大小太大,解析速度慢,虽然XPath在解析xml文档时比getElementsByTagName快许多,但XPath并未得到广泛支持

JSON相对xml来说,文件体积相对更少,通用性强

JSON数据被当成另一个JavaScript文件并作为原生代码执行,为实现这一点,这些数据必须封装在一个回调函数中,这就是所谓的JSON填充(JSON with padding)JSON-P

最快的JSON格式就是使用数组形式的JSON-P

使用JSON-P必须注意安全性,因为JSON-P必须是可执行的JavaScript,它可能被任何人调用并使用动态脚本注入技术插入到网站,另一方面,JSON在eval前是无效的JavaScript,使用XHR时它只是被当作字符串获取,所以不要把任何敏感数据编码在JSON-P中。

理想的数据格式应该是只包含必要的结构,以便你可以分解出每一个独立的字段,所以自定义格式相对来说体积更小点,可以快速下载,且易于解析(只要用split函数即可),所以当你创建自定义格式时,最重要的决定之一就是采用哪种分隔符

        var rows = req.responseText.split(/\u0001/);//正则表达式作为分隔符
var rows = req.responseText.split("\u0001");//字符串作为分隔符(更为保险)

数据格式总结

缓存数据

  • 在服务器,设置HTTP头信息以确保你的响应会被浏览器缓存
  • 在客户端,把获取到的信息存储到本地,从而避免再次请求

如果你希望Ajax响应能被浏览器缓存,请必须使用GET方式发出请求。设置Expires头信息是确保浏览器缓存Ajax响应最简单的方法,而且其缓存内容能跨页面和跨会话

当然也可以手工管理本地缓存,也就是直接把服务器接收到的数据缓存起来

用习惯了Ajax类库了,然后却连自己怎么写一个XMLHttpRequest都不知道了,事实上很多Ajax类库都有这样那样的局限(比如说不允许你直接访问readystatechange事件,这也意味着你必须等待完整的响应接收完毕之后才能开始使用它)所以......

Ajax小结

高性能JavaScript笔记二(算法和流程控制、快速响应用户界面、Ajax)的更多相关文章

  1. 高性能JS笔记4——算法和流程控制

    一.循环 for.while.do while三种循环的性能都没有多大区别.foreach 的性能较其他三种差 . 既然循环没有多大区别,注意循环内的代码控制. 减少迭代次数. 减少迭代工作量. 推荐 ...

  2. 高性能javascript笔记

    ----------------------------------------------------------- 第一章 加载和执行 ------------------------------ ...

  3. 高性能javascript学习笔记系列(4) -算法和流程控制

    参考高性能javascript for in 循环  使用它可以遍历对象的属性名,但是每次的操作都会搜索实例或者原型的属性 导致使用for in 进行遍历会产生更多的开销 书中提到不要使用for in ...

  4. golang学习笔记(二):流程控制

    欢迎访问我的博客和github! 今天咱们把烦人的事情丢一丢,继续来学习go的基础知识. 这篇文章记录go语言的流程控制和更多类型. 流程控制 for Go 只有一种循环结构:for 循环. 基本的 ...

  5. Java学习笔记之:Java流程控制

    一.介绍 Java流程控制包括顺序控制.条件控制和循环控制. 顺序控制,就是从头到尾依次执行每条语句操作.条件控制,基于条件选择执行语句,比方说,如果条件成立,则执行操作A,或者如果条件成立,则执行操 ...

  6. python的学习笔记01_3 基本运算符 流程控制if while 字符串常用办法

    基本运算符 运算符 计算机可以进行的运算有很多种,可不只加减乘除这么简单,运算按种类可分为算数运算.比较运算.逻辑运算.赋值运算.成员运算.身份运算.位运算,今天我们暂只学习算数运算.比较运算.逻辑运 ...

  7. Javascript 链式操作以及流程控制

    春节过后,感觉过年吃的油腻的食品转化的脂肪都长到 脑子去了. 根本转不动啊 上班第一天 实在是写不动代码了, 顺手打开多天为看的 收件箱,查看查看邮件,看看春节期间 风云变幻的前端圈又有哪些大事发生. ...

  8. Lua学习笔记(2): 流程控制与循环以及初涉迭代器

    条件判断语句 --if...语句 if (表达式) then --表达式为1时执行的语句 end --if...else语句 if (表达式) then --表达式为1时执行的语句 else --表达 ...

  9. C语言学习笔记(四) 流程控制

    流程控制 流程控制,说通俗一点就是程序代码执行的顺序.不管对于哪门语言来说,流程控制都是很重要的一部分内容: 流程控制的分类,可以分为三大类: 1.顺序 这个很好理解,顺序执行就是代码从上往下一行行的 ...

随机推荐

  1. HTC Vive 与Leap Motion 出现位置错误的问题

    Leap Motion已经支持VR, 但是官方没有支持HTC Vive的例子. 按照官方的文档, 其实是有问题的: https://developer.leapmotion.com/documenta ...

  2. Windows8.1画热度图 - 坑

    想要的效果 如上是silverlight版本.原理是设定一个调色板,为256的渐变色(存在一个png文件中,宽度为256,高度为1),然后针对要处理的距离矩阵图形,取图片中每个像素的Alpha值作为索 ...

  3. Entity Framework与ADO.Net及NHibernate的比较

    Entity Framework  是微软推荐出.NET平台ORM开发组件, EF相对于ado.net 的优点 (1)开发效率高,Entity Framework的优势就是拥有更好的LINQ提供程序. ...

  4. java.awt.Robot

    import java.awt.AWTException; import java.awt.Robot; import java.awt.event.KeyEvent; public class Te ...

  5. Eclipse导入 appcompat,design兼容包

    从Android studio推出1.0正式版后,就一直在as上开发项目,但是最近要测试一个项目,是eclipse结构,导入as后,是各种报错信息,决定改成eclipse. 其中项目中用到了ppcom ...

  6. Linux_脚本安装包(以Webmin的安装为例)

    1.Webmin下载地址:https://sourceforge.net/projects/webadmin/files/webmin/        版本使用1.820 2.解压Webmin     ...

  7. javascript 工具函数

    转义特殊字符为html实体 HtmlEncode: function(str){ return str.replace(/&/g, '&').replace(/\"/g, ' ...

  8. 2016年第2周读书笔记与工作笔记 scrollIntoView()与datalist元素

    这一周主要是看了html5网页开发实例与javascript 高级程序设计,供以后翻阅查找.  html5网页开发实例第1章与第二章的2.1部分: 第1章内容: html5在w3c的发展史. 浏览器的 ...

  9. 【ASP.NET Identity系列教程(二)】运用ASP.NET Identity

    注:本文是[ASP.NET Identity系列教程]的第二篇.本系列教程详细.完整.深入地介绍了微软的ASP.NET Identity技术,描述了如何运用ASP.NET Identity实现应用程序 ...

  10. 时间复杂度---我又要想起初中数学老师的脸了xxxxx

    时间复杂度: 常用的时间复杂度有:常数级,对数级,线性级 线性对数级 平方级,立方级别,多项式级别,指数级别,阶乘级别 这里我们主要探讨对数级,线性级,平方级,指数级---为什么不讨论其他的?别的我也 ...