前端工程优化:javascript的优化小结

简介:

我觉得优化javascript是一门高深的学问,在这里也只能站在前人的肩膀上,说一些我浅显的认识,更希望的是抛钻引玉,如有不对,敬请斧正。

  首先,要认识到是,优化js的关键之处在于,优化它的运行速度,以此为切入点。

  javascript的优化原则是:二八原则

  

  满足考量大多数情况,而遇到极端情况,有能力则兼顾之,学会放弃,适当取舍;

  

  原因是,影响用户的体验很重要的因素之一响应时间

  • 0.1s: 用户觉得很流畅
  • 1.0s: 用户的操作可能偶尔受到影响,并且用户已经能感觉到有些不流畅
  • 10s : 对用户的影响比较严重,需要相应的进度提示。用户也会有一些沮丧   (//我觉得10s太宽泛了,通常而言2s以上就受不了了)

 

  当然js优化只是提升响应时间需要改善的方面的众多之一,前端优化知识何其之深,只有深入了解,才会惊讶于前端所需要掌握的知识竟是如此之多,当然无形之中也会给人压力||动力。扯远了,但是还是想力荐两篇关于前端优化的blog:前端工程打开速度优化的循序渐进总结 和 web前端优化最佳实践及工具

  继续正题,ok,那么知道了目标是提升响应时间,加快运行速度,那么具体有哪些可行的方案呢:

  1. 管理作用域
  2. 操作数据
  3. 流控制
  4. Reflow
  5. DOM操作
  6. 长时间运行的脚本处理

  

  

  管理作用域

  举个板栗:

  

1
2
3
4
5
6
7
8
9
var  foo = 1;
function  test(){
     //对变量foo进行一系列操作
}
 
function  test2(){
     var  foo = 1;
      //对变量foo进行一系列操作
}

 

  也就是说,局部变量存在于活动对象中,解析器只需查找作用域中的单个对象。

  在JavaScript中,我们应该尽可能的用局部变量来代替全局变量,这句话所有人都知道,可是这句话是谁先说的?为什么要这么做?有什么根据么?不这么做,对性能到底能带来多大的损失?以下是我摘自《JavaScript Variable Performance》的一段:

  

  在如何提高JavaScript性能这个问题上,大家最常听到的建议应该就是尽量使用局部变量(local variables)来代替全局变量(global variables)。在我从事Web开发工作的九年时间里,这条建议始终萦绕在我的耳边,并且从来没有质疑过,而这条建议的基础,则来自于 JavaScript处理作用域(scoping)和标识符解析(identifier resolution)的方法。  首先我们要明确,函数在JavaScript中具体表现为对象,创建一个函数的过程,其实也就是创建一个对象的过程。每个函数对象都有一个叫做 [[Scope]]的内部属性,这个内部属性包含创建函数时的作用域信息。实际上,[[Scope]]属性对应的是一个对象(Variable Objects)列表,列表中的对象是可以从函数内部访问的。比如说我们建立一个全局函数A,那么A的[[Scope]]内部属性中只包含一个全局对象(Global Object),而如果我们在A中创建一个新的函数B,那么B的[[Scope]]属性中就包含两个对象,函数A的Activation Object对象在前面,全局对象(Global Object)排在后面。当一个函数被执行的时候,会自动创建一个可以执行的对象(Execution Object),并同时绑定一个作用域链(Scope Chain)。作用域链会通过下面两个步骤来建立,用于进行标识符解析。
  1. 首先将函数对象[[Scope]]内部属性中的对象,按顺序复制到作用域链中。
  2. 其次,在函数执行时,会创建一个新的Activation Object对象,这个对象中包含了this、参数(arguments)、局部变量(包括命名的参数)的定义,这个Activation Object对象会被置于作用域链的最前面。
  在执行JavaScript代码的过程中,当遇到一个标识符,就会根据标识符的名称,在执行上下文(Execution Context)的作用域链中进行搜索。从作用域链的第一个对象(该函数的Activation Object对象)开始,如果没有找到,就搜索作用域链中的下一个对象,如此往复,直到找到了标识符的定义。如果在搜索完作用域中的最后一个对象,也就是全局对象(Global Object)以后也没有找到,则会抛出一个错误,提示用户该变量未定义(undefined)。这是在ECMA-262标准中描述的函数执行模型和标识符解析(Identifier Resolution)的过程,事实证明,大部分的JavaScript引擎确实也是这样实现的。需要注意的是,ECMA-262并没有强制要求采用这种结构,只是对这部分功能加以描述而已。  了解标识符解析(Identifier Resolution)的过程以后,我们就能明白为什么局部变量的解析速度要比其他作用域的变量快,主要是由于搜索过程被大幅缩短了。

  也就是:当标识符解析的过程需要进行深度搜索时,会伴随性能损失,而且性能损失的程度会随着标识符深度的增加而递增。

 

 

  数据操作

  1.  使用局部变量,它是最快的

  obj.name比obj.xxx.name访问更快,访问属性的速度,与其在对象中的深度有关。“ . ”操作的次数直接影响着访问对象属性的耗时。

  2.  缓存频繁使用的对象、数组及相关的属性值

    

1
2
3
4
5
6
7
8
function  process(data){
     var  count = data.count;
     if  (count > 0){
         for ( var  i = 0; i < count ; i++){
             processData(data.item[i]);
         }      
     }
}

  3.  不直接操作NodeList,将其转换成静态数组后再使用 

  方法: Array.prototype.slice.call() => 标准浏览器

      逐个拷贝到一个新数组中 => For IE

    需要注意的是,遍历NodeList时,不做对当前NodeList相关结构有影响的DOM操作,并且如之前所提到的,要缓存一些频繁使用到的属性值,以免发生不必要的悲剧。板栗:

  

1
2
3
4
5
6
7
8
9
10
var  divs = document.getElementsByTagName( 'DIV' );
 
//假定页面中有div,所以divs.length是大于0的
for  ( var  idx = 0; idx < divs.length; idx++){
     document.body.appendChild(
         //杯具悄然而置
         document.createElement( 'DIV' )
     );
     console.info(divs.length);
}

  上面的代码最后运行会报错,原因通过不断地往document.body下插入div 节点,for循环的终止条件( div.length也随之改变)失效,陷入死循环。也就是说通过getElementsByTagName()获取得到的是一个Live NodeList的引用,任何对其相关的DOM操作都会立即反应在这个NodeList上面。

  

 

  Dom操作

  1.  增删查改

  •  尽量使用DocumentFragment
  •  处理节点时可以使用cloneNode()复制一份
  •  若要对DOM进行直接修改,请先将其display:none;

  2.  指明操作DOM的context

  context.getElementsByTagName()

  3.  拆分方法,一个方法解决一件事

  拆分功能,让一个方法只做一件事,通过不断地调用方法来实现复杂功能,但是,这些简单方法要避免相互交叉调用。

 

  Be Lazy(使脚本尽可能少地运行,或者不运行。)  

  1.  短路表达式应用:如 a && b || c
  2.   基于事件去写相应的处理方法
  3.  惰性函数

  

 

  流控制

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (...){
}
elseif(...){
}
elseif(...){
}
elseif(...){
}
elseif(...){
}
elseif(...){
}
else {
}

  原则:

  • 在if语句中,将经常会发生的条件,放在靠上的位置
  • if的条件为连续的区间时,可以使用二分法的方式来拆分
  • 较多离散值的判断,可以使用switch来替代
  • 使用数组查询的方式
  • 要注意隐式的类型转换
  • 小心递归  
1
2
3
4
5
var  foo = 0;
     
     if (foo ==  false ){  //隐式转换
         ...
      }

  

1
2
3
4
function  recurse(){
     recurse();
}
recurse();  //又是一个悲剧,会报错,无限递归了

  

 

  Reflow

  何为reflow,即是:在CSS规范中有一个渲染对象的概念,通常用一个盒子(box, rectangle)来表示。mozilla通过一个叫frame的对象对盒子进行操作。frame主要的动作有三个:

 

  • 构造frame, 以建立对象树(DOM树)
  • reflow, 以确定对象位置,或者是调用mozilla的Layout(这里是指源码的实现)
  • 绘制,以便对象能显示在屏幕上

 

  总的来说,reflow就是载入内容树(在HTML中就是DOM树)和创建或更新frame结构的响应的一种过程。

  那么造成reflow的原因有:

  • 操作DOM树
  • 与布局有关的样式改变
  • 改变className
  • 窗口大小调整
  • 字休大小

  所以若要要提高页面性能,其实就是避免reflow的开销,但是造成reflow的原因有时候是为了完成交互效果而不可避免的,所以不能说完全避免,只能尽最大限度的去减少,这就如我开头而言的二八原则。

 

  以下是一些简单的指导方针可以帮助你页面上的回流(reflow)减到最小。

 

  • 减少不必要的DOM深度。因为无论你改变DOM节点树上任何一个层级都会影响节点树的每个层级——从根结点一直到修改的子节点。不必要的节点深度将导致执行回流时花费更多的时间。
  • 精简css,去除没有用处的css
  • 如果你想让复杂的表现发生改变,例如动画效果,那么请在这个流动线之外实现它。使用position-absolute或position-fixed来实现它。
  • 避免不必要的复杂的css选择符,尤其是使用子选择器,或消耗更多的CPU去做选择器匹配。

  

    转载注明出处:http://www.cnblogs.com/coco1s/p/3946435.html

  


本文转自ChokCoco博客园博客,原文链接:http://www.cnblogs.com/coco1s/p/3946435.html

目录
相关文章
|
8天前
|
缓存 监控 前端开发
【Flutter 前端技术开发专栏】Flutter 应用的启动优化策略
【4月更文挑战第30天】本文探讨了Flutter应用启动优化策略,包括理解启动过程、资源加载优化、减少初始化工作、界面布局简化、异步初始化、预加载关键数据、性能监控分析以及案例和未来优化方向。通过这些方法,可以缩短启动时间,提升用户体验。使用Flutter DevTools等工具可助于识别和解决性能瓶颈,实现持续优化。
【Flutter 前端技术开发专栏】Flutter 应用的启动优化策略
|
1天前
|
前端开发 JavaScript UED
如何优化前端网页加载速度
在当今互联网时代,网页加载速度是用户体验的关键因素之一。本文将探讨如何通过优化前端技术,提升网页加载速度,包括压缩资源、使用CDN加速、减少HTTP请求等方法。
|
2天前
|
缓存 前端开发 JavaScript
优化前端性能的五大技巧
在当今快节奏的网络世界中,优化前端性能是网站开发中至关重要的一环。本文将介绍五种有效的技巧,帮助开发者提升前端性能,提升用户体验和网站效率。
|
6天前
|
JavaScript 前端开发 C++
【Web 前端】JavaScript window.onload 事件和 jQuery ready 函数有何不同?
【5月更文挑战第2天】【Web 前端】JavaScript window.onload 事件和 jQuery ready 函数有何不同?
|
7天前
|
JavaScript 前端开发 开发者
【Web 前端】什么是JS变量提升?
【5月更文挑战第1天】【Web 前端】什么是JS变量提升?
【Web 前端】什么是JS变量提升?
|
8天前
|
JavaScript 前端开发 IDE
【JavaScript与TypeScript技术专栏】使用TypeScript优化JavaScript应用性能
【4月更文挑战第30天】本文探讨了如何使用TypeScript优化JavaScript应用性能。TypeScript通过静态类型检查、更好的编译器优化和IDE支持提升代码稳定性和开发效率。利用类型注解、泛型、类与继承以及枚举和常量,开发者能构建高效、灵活和健壮的代码。逐步将TypeScript引入现有JavaScript项目,并通过案例分析展示性能提升效果。随着TypeScript社区的发展,它将在Web开发性能优化中扮演更重要角色。
|
8天前
|
缓存 前端开发 JavaScript
【JavaScript 技术专栏】JavaScript 前端路由实现原理
【4月更文挑战第30天】本文探讨了JavaScript前端路由在SPA中的重要性,阐述了其基本原理和实现方式,包括Hash路由和History路由。前端路由通过监听URL变化、匹配规则来动态切换内容,提升用户体验和交互性。同时,文章也提到了面临的SEO和页面缓存挑战,并通过电商应用案例分析实际应用。理解并掌握前端路由能助开发者打造更流畅的单页应用。
|
8天前
|
存储 缓存 前端开发
【Flutter前端技术开发专栏】Flutter中的图片加载与缓存优化
【4月更文挑战第30天】本文探讨了 Flutter 中如何优化图片加载与缓存,以提升移动应用性能。通过使用图片占位符、压缩裁剪、缓存策略(如`cached_network_image`插件)以及异步加载和预加载图片,可以显著加快加载速度。此外,利用`FadeInImage`、`FutureBuilder`和图片库等工具,能进一步改善用户体验。优化图片处理是提升Flutter应用效率的关键,本文为开发者提供了实用指导。
【Flutter前端技术开发专栏】Flutter中的图片加载与缓存优化
|
8天前
|
缓存 前端开发 数据安全/隐私保护
【Flutter 前端技术开发专栏】Flutter 中的键盘处理与输入框优化
【4月更文挑战第30天】本文探讨了Flutter中键盘处理与输入框优化的关键技术,包括监听键盘显示隐藏、焦点管理、键盘类型适配、输入框高度自适应、处理键盘遮挡问题及性能优化。通过使用WidgetsBindingObserver、FocusNode和TextInputType等工具,开发者能提升用户体验,确保输入框在各种场景下的良好表现。实例分析和实践建议有助于开发者将这些方法应用于实际项目。
【Flutter 前端技术开发专栏】Flutter 中的键盘处理与输入框优化
|
8天前
|
前端开发 UED 开发者
【Flutter前端技术开发专栏】Flutter中的列表与滚动视图优化
【4月更文挑战第30天】Flutter开发中,优化列表和滚动视图至关重要。本文介绍了几种优化方法:1) 使用`ListView.builder`和`GridView.builder`实现懒加载;2) 复用子组件以减少实例创建;3) 利用`CustomScrollView`和`Slivers`提升滚动性能;4) 通过`NotificationListener`监听滚动事件;5) 使用`KeepAlive`保持列表项状态。掌握这些技巧能提升应用性能和用户体验。
【Flutter前端技术开发专栏】Flutter中的列表与滚动视图优化