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

xiaoxiao2021-02-27  276

第4章 算法和流程控制

内容:

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

知识点:

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的调用栈是与系统空闲内存有关,其他浏览器都有固定数量的调用栈限制;

浏览器调用栈错误提示错误类型IEStack overflow at line x常规ErrorFirefoxToo much recursionInternalErrorSafariMaximum call stack size exceededRangeErrorChrome不提示RangeErrorOperaAbort(control stack overflow)终止js引擎,不抛出错误

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

总结:

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

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

内容:

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

知识点:

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

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

内容:

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

知识点:

用于执行js和更新UI的进程被称为“浏览器UI线程”,它的工作基于一个简单的队列系统,任务会被保存到队列中直到进程空闲;任务类型包括:运行js代码、执行UI更新;js执行期间不会更新UI,UI更新期间不会执行js;js与UI共享同一进程的部分原因是它们之间会频繁访问;浏览器对脚本运行的限制有:调用栈大小限制、运行时间限制,各浏览器的限制如下: 浏览器限制类型修改方法IE500万条语句修改注册表MaxScriptStatements字段Firefox10秒修改浏览器配置,输入about:conf修改dom.max_script_run_timeSafari5秒无法修改,可以禁用定时器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; 当任务与UI无关且不能被分解时可考虑使用Worker Web Worker运行环境: navigator,包含属性:appName、appVersion、user Agent、platformlocation,只读self,指向全局worker对象importScripts(),用来加载外部js文件,阻塞式,直到执行完成才继续所有ES对象,Object、Array、Date等XMLHttpRequestsetTimeout()、setInterval()close(),终止Worker运行使用Worker需要单独建立js文件然后使用:var worker = new Worker('code.js'),文件会异步下载,文件下载执行完成后才会启动此Worker;主进程端: 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); }

总结:

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

第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可以减少请求数并处理一个响应中的各种文件类型,但不能缓存接收到的响应

总结:

减少请求数,合并js和css文件或使用MXHR;页面主要内容加载完成后用ajax获取次要文件;确认代码错误不会输出给用户,并在服务端处理错误;可以使用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

总结:

避免使用给eval()、Function()构造器、setTimeout()、setInterval()传入字符串来造成双重求值,因为创建新的解释器会带来性能损耗,setTimeout()和setInterval()应传递函数;尽量使用直接量创建对象和数组,直接量的创建和初始化比非直接量形式快;使用延迟加载和条件预加载避免重复的检测工作;进行数学运算时考虑使用直接操作数字的二进制形式的位运算,尽量使用原生方法。
转载请注明原文地址: https://www.6miu.com/read-5622.html

最新回复(0)