js性能优化--学习笔记
《高性能网站建设进阶指南》:
1.使用局部变量,避免深入作用域查找,局部变量是读写速度最快的;把函数中使用次数超过一次的对象属性和数组存储为局部变量是一个好方法;比如for循环中的.length通常可以保存为局部变量。
2,多个if时,if顺序按概率降序排列
3,当仅判断一两个条件时,if通常比switch更快,当有两个以上条件且条件比较简单(不是范围判定)时,switch通常更快;因为多数情况下,switch语句中执行单个条件的时间比if短;
4,循环中把循环变量递减到0,而不是递增到长度,把条件改成了与0比较,速度更快,如for(var i=arr.length;i--){};
5,避免for in;for in是用来遍历对象的可枚举属性,需要遍历整个原型链来查找可枚举属性,若
Array.prototype.myf=function(){};//(数据属性默认都是可枚举的)
var a=['a','b'];for(var p in a){console.log(p+','+a[p]);}
输出将是:
0,a
1,b
myf,function (){}
对于object同样如此,所以一般需要用hasOwnProperty来确保只变例对象实例自身的属性。
若已知对象的属性,则采用普通的for循环性能优于for in;如var arr=['p1','p2','p3'];for(){process(obj[arr[i]]);}
6,性能优化:拆分js,减少初次render的下载量,注意会出现用户点击时,事件绑定还没完成的情况。js延迟下载;
7,css选择符;匹配顺序是从右到左的;避免通配符,用classname代替标签名,利用属性继承。。。。。。
《高性能javascript》:
V8 引擎由以下几个核心部分组成:
- 基本编译器(base compiler),在你的代码运行之前,它会分析你的 JavaScript 代码并生成本地机器码,而不是执行字节码或简单地解释它。这种机器码起初是没有被高度优化的。
- V8 将对象解析为对象模型(object model)。对象是在 JavaScript 中是以关联数组的方式呈现的,但是在 V8 引擎中,它们是通过隐藏类(hidden classes)的方式来表示的。这是一种可以优化查找的内部类型机制。
- 一个运行时分析器(runtime profiler),它监视正在运行的系统,并标识 “hot” 函数,也就是那些最后会花费大量运行时间的代码。
- 一个优化编译器(optimizing compiler),它重新编译和优化那些被运行时分析器标识为 “hot” 的代码,并进行 “内联” 等优化(例如,在函数被调用的地方用函数主体去取代)。
- V8支持逆优化(deoptimization),这意味着,如果优化编译器发现在某些假定的情况下,把一些已经优化的代码进行了过度的优化,它会舍弃优化后的代码。
- 垃圾回收器,理解它的运作原理和理解如何优化你的JavaScript代码同等重要。
垃圾回收是内存管理的一种机制,垃圾回收器的概念是,它将试图回收那些不再被使用的对象所占据的内存,在像 JavaScript 这种支持垃圾回收的语言中,如果程序中仍然存在指向一个对象的引用,那么该对象将不会被回收。
在大多数情况下,我们没有必要去手动得解除对象的引用(de-referencing)。只需要简单滴将变量放在需要它们的位置(在理想的情况下,尽可能使用局部变量,也就是说,在它们被使用的函数中声明它们,而不是在更外层的作用域),垃圾就能正确地被回收。
在 JavaScript 中,强制进行垃圾回收是不可能的,而且你也不应该尝试这样做,因为垃圾回收是由运行时控制,并且它通常知道垃圾回收的最佳时机。在可能的情况下,尽量避免使用 delete
,
尽管如此,你肯定会发在许多流行的 JavaScript 库中使用了 delete - 这有它语言目的。这里的主旨是,避免在运行时修改 “hot” 对象的结构,JavaScript 引擎可以检测到这些 “hot” 的对象,并尝试对其进行优化。如果在对象的生命期中没有遇到重大的结构改变,引擎的检测和优化过程会来得更加容易,而使用 delete
则会触发对象结构上的这种改变。
不少人对 null
的使用上也存在误解。将一个对象引用设置为 null
,并不是意味着“清空”该对象,只是将它的引用指向 null
。使用 o.x = null
比使用 delete
会更好些,但这甚至可能也是不必要的。
如果此引用是当前对象的最后引用,那么该对象就满足了垃圾回收的资格。如果此引用不是当前对象的最后引用,则该对象是可访问的,而不会被垃圾回收。
另外需要注意的是,全局变量在页面的生命周期中是不会被垃圾回收器清理的。只要页面保持打开状态,全局对象就会常驻在内存当中。只有当刷新页面、导航到其他页面、关闭标签页或退出浏览器时,全局变量才会被清理。函数作用域的变量超出作用域范围时,它就会被清理。当函数完全结束,并且再没有任何引用指向其中的变量,函数中的变量会被清理。
经验法则:
为了使垃圾回收器尽早回收尽可能多的对象,请不要保留(hold on)不再需要的对象。(qi:比如在函数里return一个不需要的变量,因为这会导致变量不会被回收)这里有几点需要谨记:
- 就像之前所说的那样,比手动删除变量引用更好的方式是,在恰当的作用域中使用变量,例如,尽量在函数作用域中声明变量,而尽可能不要声明不会被回收的全局变量,这将意味着更干净更省心的代码。
- 确保解绑那些不再需要的事件监听器,尤其是那些即将被移除的 DOM 对象所绑定的事件。
- 如果你正在使用数据缓存,确保手动清理缓存或者使用衰老机制,避免缓存中储存大量不会被重用的数据。
数组字面量非常有用,它可以暗示数组的大小和类型。它通常用在体积不大的数组中。
// V8 知道你需要一个长度为 4 并且储存数字的数组:
|
qi:闭包也会导致变量不会被回收,事件委托:$('table').on('click', td, ...)优于$('table td').on('click', ...)
当浏览器重新渲染文档中的元素时需要重新计算它们的位置和几何形状时,我们称之为回流。回流会阻塞用户在浏览器中的操作,因此理解提升回流时间是非常有帮助的。使用document.createDocumentFragment()创建了一虚拟的节点对象(节点对象包含所有属性和方法),保存多个小节点后在append;在所有节点类型中,只有文档片段DocumentFragment在文档中没有对应的标记。DOM规定文档片段(documentfragment)是一种”轻量级“的文档,可以包含和控制节点,但不会像完整的文档那样占用额外资源。即当请求把一个 DocumentFragment 节点插入文档树时,插入的不是 DocumentFragment 自身,而是它的所有子孙节点。这使得 DocumentFragment 成了有用的占位符,暂时存放那些一次插入文档的节点。它还有利于实现文档的剪切、复制和粘贴操作,尤其是与 Range 接口一起使用时更是如此.
qi: css性能:尽量repaint而不是reflow.
显式设置图片的宽高:
当浏览器加载页面的HTML代码时,有时候需要在图片下载完成前就对页面布局进行定位。如果HTML里的图片没有指定尺寸(宽和高),或者代码描述的尺寸与实际图片的尺寸不符时,浏览器则要在图片下载完成后再“回溯”该图片并重新显示,这会消耗额外时间。所以,最好为页面里的每一张图片都指定尺寸,不管是在页面HTML里的<img>标签,还是在CSS里。
浏览器解析页面顺序:参考http://www.alloyteam.com/2015/05/wang-ye-xing-neng-zhi-html-css-javascript/;
qi:浏览器得到某个页面后,按顺序开始解析,遇到css file和image,发相应的请求,同时继续往下,到结束就构建好了无样式的dom tree,css file下载后解析得到CSSOM,结合dom tree开始渲染得到render tree,至此在chrome developer tool里就是domComplete完成的时间(蓝色竖线),等image下载好插入render tree,至此在chrome developer tool里就是domLoader完成的时间(红色竖线).因为js有可能改变dom tree结构,所以如果遇到js file会暂时停止其他渲染行为,等js解析完成后再渲染。 使用async标识的javascript,浏览器将异步执行javascript不会阻塞正常的dom渲染。