读书笔记 — 《高性能JavaScript》4-8章

来源:互联网 发布:mac信仰灯会影响屏幕吗 编辑:程序博客网 时间:2024/05/21 08:00

第4章 算法和流程控制

内容:

程序算法和流程优化,包括优化循环、递归等等

知识点:

  1. js有四种循环类型:
for(var i=0; i<10; i++) {};   //初始化、前测条件、后执行体、循环体while() {}   //前侧条件、循环体do{} while();   //循环体、后测条件for(var prop in object) {}

for-in循环所返回的对象包括实例属性和从原型链中继承的属性,它是四种循环中最慢的,大约只有其他类型速度的1/7;
2. 优化循环有两种方式:
减少迭代工作量 ->复杂度=0(n)时首选
减少迭代次数 ->复杂度>0(n)时首选
3. 减少迭代工作量方法:
在初始化中将length保存为局部变量,例如:for(let i=0, len=arr.length; i<len; i++)
使用倒序循环,例如:for(let i=10;i--;){}while(j--){},每次循环减少一次控制条件判断(迭代次数少于上限?值是否为true?=>值是否为true?);
4. 减少迭代次数方法:
“达夫设备”,每次循环执行多次操作,在迭代次数>1000时考虑使用;
5. 大多数情况switch比if-else更快,但只要当条件数量很大时才慢得明显。因为大多数语言对switch采用分支表索引进行优化,并且在js中在switch中使用全等操作符,不会发生类型转换;
6. 条件语句选择:
离散值 => switch
多个值域 => if-else
大量离散值且条件和值存在映射关系 => 查找表(存入数组直接return)
7. 优化if-else方法:最可能出现的条件放在首位,嵌套if-else(二分法);
8. 除IE的调用栈是与系统空闲内存有关,其他浏览器都有固定数量的调用栈限制;

浏览器 调用栈错误提示 错误类型 IE Stack overflow at line x 常规Error Firefox Too much recursion InternalError Safari Maximum call stack size exceeded RangeError Chrome 不提示 RangeError Opera Abort(control stack overflow) 终止js引擎,不抛出错误

9. 两种递归模式:直接递归模式、隐伏模式;

总结:

  1. 避免使用for-in循环,除非需要遍历一个属性数量未知的对象;
  2. 改善循环的最佳方式是减少迭代工作量和减少迭代次数;
  3. 在判断条件较多时,使用查找表比if-else和switch更快;
  4. 使用递归时注意浏览器调用栈限制;
  5. 遇到栈溢出错误时可将方法改为迭代算法,或用Memoization避免重复计算。

第5章 字符串和正则表达式

内容:

字符串操作优化和正则表达式查找优化,包括优化字符串连接、正则匹配等等

知识点:

  1. 例如str+='one' + 'two',js会创建一个临时字符串保存onetwo的连接然后与str连接再赋值给str,例如使用str=str + 'one' + 'two'可以避免创建临时字符串,因为会从左至右依次连接,但是改变连接顺序例如str='one' + str + 'two'则优化失效;
  2. IE7及以前版本的字符串连接使用了非常糟糕的方法:每连接一对字符串都把它复制到一块新分配的内存中;IE8的实现则是:记录现有字符串的引用来构造新字符串,当需要使用时才将各部分逐个拷贝到一个新的字符串中,然后用它取代之前的字符串引用;
  3. Array.prototype.join方法在大多浏览器中比常规字符串连接更慢,但在IE7及更早版本中是一种优化手段;
  4. 正则优化部分待续

第6章 快速响应的用户界面

内容:

使用各种技术提升应用交互性,包括合理管理UI线程、使用Web Worker进行额外计算等等

知识点:

  1. 用于执行js和更新UI的进程被称为“浏览器UI线程”,它的工作基于一个简单的队列系统,任务会被保存到队列中直到进程空闲;
  2. 任务类型包括:运行js代码、执行UI更新;js执行期间不会更新UI,UI更新期间不会执行js;js与UI共享同一进程的部分原因是它们之间会频繁访问;
  3. 浏览器对脚本运行的限制有:调用栈大小限制、运行时间限制,各浏览器的限制如下:
浏览器 限制类型 修改方法 IE 500万条语句 修改注册表MaxScriptStatements字段 Firefox 10秒 修改浏览器配置,输入about:conf修改dom.max_script_run_time Safari 5秒 无法修改,可以禁用定时器 Chrome 依赖通用崩溃检测系统处理 Opera 无限制

4. 单个js操作花费总时间不应该超过100ms;
5. 有些浏览器在js运行期间不会把UI更新任务加入队列,这是为了保证UI页面的动态变化,所以可能出现脚本运行期间点击按钮,无法看到它被按下的样式,但它的onlick事件处理器会被执行;
6. 使用定时器管理UI线程:
- 定时器从调用它时开始算,计时完成后被加入UI队列,但需要注意函数只有在创建它的函数执行完成之后才有可能被执行
- 定时器会重置所有相关的浏览器限制,包括长时间运行脚本定时器,调用栈也会重置为0
- js定时器通常不太精确,相差大约几毫秒。因为例如windows系统定时器分辨率是15ms,即一个15ms的定时器会根据最后一次系统时间刷新而转换为0或15。最小值最好为25ms(实际时间15或30)以确保至少有15ms的延时,再小的延时对大多数UI更新会不够用
7. 记录代码运行时间:

var start = +new Date(),    stop;process();stop = +new Date();var time = stop - start;
  1. 当任务与UI无关且不能被分解时可考虑使用Worker
    Web Worker运行环境:
    • navigator,包含属性:appName、appVersion、user Agent、platform
    • location,只读
    • self,指向全局worker对象
    • importScripts(),用来加载外部js文件,阻塞式,直到执行完成才继续
    • 所有ES对象,Object、Array、Date等
    • XMLHttpRequest
    • setTimeout()、setInterval()
    • close(),终止Worker运行
  2. 使用Worker需要单独建立js文件然后使用:var worker = new Worker('code.js'),文件会异步下载,文件下载执行完成后才会启动此Worker;
  3. 主进程端:
var worker = new Worker('code.js');worker.onmessage = function(e) {    console.log(e.data);}worker.postMessage("s");

Worker端:

self.onmessage = function(e) {    self.postMessage(e.data);}

总结:

  1. 寻求运行速度和用户体验的平衡,当js任务执行超过100ms时,就应该考虑使用定时器或者Web Worker;
  2. 可以将任务分解成一个一个的小块分别执行;

第7章 Ajax

内容:

使用各种技术提升应用交互性,包括合理管理UI线程、使用Web Worker进行额外

知识点:

1 向服务器请求数据的常用技术:
- XHR
- 动态脚本注入
- Multipart XHR
- iframes
- Comet
2 GET请求具有幂等性,对服务器无副作用,经GET请求的数据会被缓存起来;
3 IE限制URL长度,当URL长度超过2048个字符(4k)时应使用POST;
4 可以使用“流”分段处理返回数据,低版本IE不支持“流”,也不提供readyState为3的状态;
5 使用动态脚本注入有很多限制,包括不能设置请求头,只能用GET,不能设置请求超时或重试,必须等所有数据返回才能访问等等,实现JSONP(JSON with padding)跨域:

//js端var scriptEle = document.createElement('script');scriptEle.src = "url";document.getElementsByTagName('head')[0].appendChild(scriptEle);function callBack(jsonString) {    var data = eval('(' + jsonString + ')');}//服务端返回callBack({"status":1, "data": 2});

6 可以将参数以var params = ['key1=value1','key2=value2']格式存为数组,最后params .join(‘&’)拼接;
7 GET只发送一个数据包,而POST至少发送两个(请求头、请求正文),所以GET请求速度会更快;
8 向服务器发送数据技术:XHR、Beacons(信标)
9 Beacons实例:(new Image()).src = url + '?' + params .join('&');
是给服务器回传信息最有效的方式,性能消耗小且服务端错误不会影响客户端;
接收服务器返回数据:监听Image对象的load事件;检查服务器返回图片的宽高(如可以用宽1表示成功,2表示重试);
10 XML优点:
- 极佳的通用性,服务端和客户端都完美支持
- 格式严格
- 易于验证
但也存在过于冗长,语法模糊的问题,解析它也需要提前知道它的结构
11 XHR获取的JSON是字符串格式,而JSON-P本身就被当做JSON对象;
12 两种情形避免使用JSON-P,因为JSON-P必须是可执行js,可能被任何人调用并使用动态脚本注入技术插入任何网站;不能把敏感数据编码在JSON-P中,因为你无法确认它是否保持私有调用状态,即使带有随机URL和做了cookie判断;
13 最快的ajax请求就是没有请求,减少不必要请求方法:
- 服务端,设置HTTP头信息确保响应被浏览器缓存(设置Expires头信息,GMT日期格式)
- 客户端,把获取的信息存储到本地,避免再次请求
14 大多数浏览器支持XMLHttpRequest对象,老版本浏览器使用ActiveX对象,并需要传入版本号,通用获取xhr对象方法:

function createXhrObject(){    var msxml_progid = [        'MSXML2.XMLHTTP.6.0',        'MSXML3.XMLHTTP',        'Microsoft.XMLHTTP',   //不支持readyState 3        'MSXML2.XMLHTTP.3.0',   //不支持readyState 3    ];    var req;    try{        req = new XMLHttpRequest();    }    catch(e){        for(var i = 0, len = msxml_progid.length; i<len; i++){            try{                req = new ActiveXObject(msxml_progid[i]);                break;            }            catch(e2){}        }    }    finally{        return req;    }}

15 各种交互手段比较:
- XML支持良好,但笨重且解析缓慢;JSON轻量级解析快,但需要编写额外的服务端构造程序;
- 动态脚本注入允许跨域请求和本地执行js和JSON,但它的接口不安全,而且不能读取头信息或响应代码;
- Multipart XMR可以减少请求数并处理一个响应中的各种文件类型,但不能缓存接收到的响应

总结:

  1. 减少请求数,合并js和css文件或使用MXHR;
  2. 页面主要内容加载完成后用ajax获取次要文件;
  3. 确认代码错误不会输出给用户,并在服务端处理错误;
  4. 可以使用ajax类库,但必要时可编写自己的底层ajax代码。

第8章 编程实践

内容:

一些编程中需要注意的优化细节,包括条件预加载、使用底层方法等等。

知识点:

1 传入字符串并动态执行的方法:eval()、Function()构造函数、setTimeout()、setInterval();
2 每次调用eval()都要创建一个新的解释器实例;
3 避免重复的条件判断:延迟加载、条件预加载
当一个函数在页面中不会立刻调用时,延迟加载是最好的选择:

function addHandler(target, eventType, handler){    if(target.addEventListener){        //复写函数        addHandler = function(target, eventType, handler){            target.addEventListener(eventType, handler, false);        }    }else{        addHandler = function(target, eventType, handler){            target.attachEvent("on" + eventType, handler);          }    }    //调用新函数    addHandler(target, eventType, handler);}

条件预加载:函数定义时判定

var addHandler = document.body.addEventListener?    function(target, eventType, handler){        target.addEventListener(eventType, handler, false);    }:    function(target, eventType, handler){        target.attachEvent("on" + eventType, handler);      }}

4 js引擎是由低级语言构建的而且经过编译,使用位操作符和原生方法例如Math中的方法和querySelectorAll()速度更快;
5 位操作符应用:i&1可以判断奇偶,奇时返回true,偶时返回false

总结:

  1. 避免使用给eval()、Function()构造器、setTimeout()、setInterval()传入字符串来造成双重求值,因为创建新的解释器会带来性能损耗,setTimeout()和setInterval()应传递函数;
  2. 尽量使用直接量创建对象和数组,直接量的创建和初始化比非直接量形式快;
  3. 使用延迟加载和条件预加载避免重复的检测工作;
  4. 进行数学运算时考虑使用直接操作数字的二进制形式的位运算,尽量使用原生方法。
0 0
原创粉丝点击