找回密码
 立即注册
首页 业界区 业界 web移动开发最佳实践之js篇

web移动开发最佳实践之js篇

韶又彤 2025-5-29 16:28:41
一、js概述
  js即JavaScript,是被设计用来验证表单、检测浏览器、创建cookies、改进设计以及更多应用的网络脚本语言,它非常容易使用。在web应用中,它是主要的编程语言,主要用途是进行各种逻辑控制行为展现等。对于js的优化,对于整个应用的提升都是非常显著的。
二、使用字面量(literal notation)来声明对象和数组
  创建对象和数组的方法有很多,但是使用字面量是最简单的。传统的方法是使用内建的构造器声明:
  1. //create an object
  2. var obj = new Object();
  3. obj.debug = false;
  4. obj.lang = "en";
  5. //create an array
  6. var arr = new Array("one", "two", "three");
复制代码
这种方式在技术上是没问题的,但是使用字面量声明会更快而且代码更少
  1. //create an object
  2. var obj = {debug: false, lang: "en"};
  3. //create an array
  4. var arr = ["one", "two", "three"];
复制代码
三、避免使用全局变量和函数
  即把属性方法都绑定到一个命名空间对象里,这样不仅可以减少命名冲突,而且可以提升程序性能
  当两个区域的代码使用同一个全局变量名作不同用途时,就会产生命名冲突。在JavaScript里,函数外定义的变量或对象都是全局的,随着程序代码和库的增加,命名冲突的概率就越大。如果函数内或其他区域的代码引用了一个特定的全局变量,脚本引擎就必须遍历一遍作用域直到找到这个变量,局部变量则更容易找到。全局变量会在整个脚本的生命周期中存在,但是局部变量会及时被垃圾收集器回收。
例如以下使用全局的声明(不高效):
  1. //define global variables
  2. var lang = "en";
  3. var debug = true;
  4. //define global function
  5. function setLang (arg) {
  6.     lang = arg;
  7. }
复制代码
使用如下声明则更好
  1. var myApp = {
  2.     lang: "en",
  3.     debug: true,
  4. };
  5. myApp.setLang = function (arg) {
  6.     this.lang = arg;
  7. }
复制代码
四、高效的使用try catch语句
  你可以使用try-catch语句来拦截程序抛出的错误(在浏览器处理之前),这对于向用户隐藏错误或者为用户定制错误信息是很有用的。
  当try结构中发生错误时,程序会立即停止跳到catch结构(会提供错误对象)中。在catch结构中,错误对象会赋给一个新的变量,新的变量在catch结构中一直存在,直到catch语句结束。创建并处理这个新的运行时变量会影响到程序的性能,在关键功能循环中应避免使用try-catch结构。例如:
  1. var object = ['foo', 'bar'], i;
  2. for (i = 0; i < object.length; i++) {
  3.    try {
  4.       // do something
  5.    } catch (e) {
  6.       // handle exception
  7.    }
  8. }
复制代码
以上这段代码可能会抛出多个错误,这样写可能会更好
  1. var object = ['foo', 'bar'], i;
  2. try {
  3.     for (i = 0; i < object.length; i++) {
  4.         // do something
  5.     }
  6. } catch (e) {
  7.     // handle exception
  8. }
复制代码
五、使用赋值运算来连接字符串
  字符串连接是很常用的操作,也有很多种方式,比如:
  1. //Using the concatenation (+) operator
  2. str = "h" + "e";  
  3. //Using the shorthand assigment (+=) operator
  4. str += "l";            
  5. //Using string.concat()
  6. str = str.concat("l", "o");        
  7. //Using array.join()
  8. str = ["h", "e", "l", "l", "o"].join("");   
复制代码
如果你执行的连接操作次数较少,那么以上任何一种方式都可以。但是,当执行大量的连接操作时,就需要优化一下了:
  1. //Slower: Concatenating strings with + operator
  2. str += "x" + "y";
复制代码
以上连接操作比较,它会按以下步骤执行(参见‘编译原理’):

  • 创建一个临时变量
  • 连接后的字符串xy被赋给这个临时变量
  • 临时变量与str的当前值相加
  • 结果赋给str变量
你可以使用如下的方式避免使用临时变量(减少内存的使用):
  1. str += "x";
  2. str += "y";
复制代码
六、优化你的循环
  当你使用循环的时候,你可以通过减少每次迭代时工作量来优化循环的整体性能。例如:
  1. for (var i = 0; i < arr.length; i++) {
  2.     // length of arr is recalculated every time
  3. }
复制代码
在以上代码中,arr.length在每次循环中都被计算了一次,这是不必要的,可以声明一个局部变量len来缓存这个值,就会提高运行速度:
  1. for (var i = 0, len = arr.length; i < len; i++) {
  2.     // cache the length of the array
  3. }
复制代码
或者为了进一步优化,考虑反向的执行循环(如果不关心数组成员的顺序的话):
  1. for (var i = arr.length; i--;) {
  2.     // in reverse
  3. }
复制代码
七、避免使用eval()方法
  eval()方法可以执行一段JavaScript代码,应该避免使用的原因:

  • 性能较差,它必须调用编译器来传递其参数,然后执行
  • 安全问题,因为它会执行传递给它的任何代码,所以容易受各种注入攻击,特别是在来源未知的时候
  • 不利于调试,eval的参数是动态产生的,调试起来不方便,可读性也较差
  1. //Incorrect usage: Using eval to set a value
  2. eval("myValue = myObject." + myKey + ";");
  3. //Correct usage: Using subscript notation to set a value
  4. myValue = myObject[myKey];
复制代码
另外timeout函数中的setTimeout()和setInterval()也可以接受字符串参数,然后执行,因此表现跟eval()一样。应该避免传递字符串,如下:
  1. // Incorrect usage: Passing a string to setInterval()
  2. var oElement = null;
  3. setInterval('oElement = document.getElementById("pepe");', 0);
  4. // Correct usage: Passing a function to setInterval()
  5. var oElement = null;
  6. setInterval(function() {
  7.     oElement = document.getElementById("pepe");
  8. }, 0);
复制代码
八、使用事件委托
  在处理DOM事件的时候,你可以仅对一个父元素绑定一个事件而不是每一个子元素。这种技术即事件委托,它利用事件冒泡来分配事件处理程序,可以提高脚本的性能。比如,一个div元素下面有10个按钮,你可以给div绑定一个监听事件,而不是给10个按钮分别绑定一个事件。传统的声明方式:
  1. Click
  2. <button id="btn1" onclick="handleClick();">One</button>
  3. <button id="btn2" onclick="handleClick();">Two</button>
复制代码
为了提高代码的性能,我们可以加一个div父元素,事件会向上冒泡,直到被处理。事件对象是触发事件的元素,我们可以根据它的id属性来判断是哪一个元素触发了事件:
  1.   <button id="btn1">One</button>
  2.   <button id="btn2">Two</button>
  3. document.getElementById("btngroup").addEventListener("click", function (event) {
  4.   switch (event.srcElement.id) {  //firefox 下为 event.target.id
  5.   case "btn1":
  6.     handleClick();
  7.     break;
  8.   default:
  9.     handleClick();
  10.   }
  11. }, false); // type, listener, useCapture (true=beginning, false=end)
复制代码
九、尽量减少DOM操作
  DOM是一个包含了很多信息的复杂的API,因此即使是很小的操作可能会花费较长的时间执行(如果要重绘页面的话)。为了提高程序性能,应尽量减少DOM操作,这里有一些建议:
1.减少DOM的数目
DOM节点的数目会影响与它相关的所有操作,要尽量使DOM树小一些:

  • 避免多余的标记和嵌套的表格
  • 元素数尽量控制在500个以内(document.getElementsByTagName('*').length)
2.缓存已经访问过的节点
当访问过一个DOM元素后,就应该把它缓存起来,因为你的程序往往要重复访问某个对象的,例如:
  1. for (var i = 0; i < document.images.length; i++) {
  2.     document.images[i].src = "blank.gif";
  3. }
复制代码
以上例子中,docum.images对象被访问了多次,这并不高效,因为每一次循环中,浏览器都要查找这个元素两次:第一次读取它的长度,第二次改变相应的src值。更好的做法是先把这个对象存储起来:
  1. var imgs = document.images;
  2. for (var i = 0; i < imgs.length; i++) {  //当然也可以把 imgs.length 提前算出来,这里不是重点
  3.     imgs[i].src = "blank.gif";
  4. }
复制代码
十、减少页面重绘
  在控制DOM元素数目的同时,你还可以通过减少修改元素(减少页面的重绘)的方法来提高性能。重绘有两种方式:repaintreflow
1.repaint,也叫redraw,即改变了元素的视觉效果,但是不影响它的排版(比如改变背景颜色)
2.reflow,会影响部分或者全部页面的排版,浏览器不仅要计算该元素的位置,还要计算它影响到的周围的元素位置
当你要改变页面布局的时候,reflow就发生了,主要有如下情况:

  • 增加或删除DOM节点
  • 改变元素的位置
  • 改变元素的尺寸(如margin,padding,border,font,width,height等)
  • 调整浏览器窗口的尺寸
  • 增加或删除css
  • 改变内容(如用户输入表单)
  • 命中css选择器(如hover)
  • 更改了class属性
  • 利用脚本更改了DOM
  • 检索一个必须被计算的尺寸(如offsetWidth,offsetHeight)
  • 设置了一个css属性
这里有一些减少页面重绘的建议:
css的建议:

  • 改变class属性时应尽量少的影响到周围的元素节点
  • 避免声明多个内联的样式(把多个样式放在一个外部文件里)
  • 有动画的元素使用绝对定位,这样不会影响其他元素
  • 避免使用table来排版,如果需要使用保存数据,那么要固定排版(table-layout:fixed)
js的建议:

  • 缓存计算过的样式
  • 对于固定的样式,改变class的名词而不是样式;对于动态的样式,改变cssText属性:
    1. // bad - changing the stle - accessing DOM multiple times
    2. var myElement = document.getElementById('mydiv');
    3. myElement.style.borderLeft = '2px';
    4. myElement.style.borderRight = '3px';
    5. myElement.style.padding = '5px';
    6. // good - use cssText and modify DOM once
    7. var myElement = document.getElementById('mydiv');
    8. myElement.style.cssText = 'border-left: 2px; border-right: 3px; padding: 5px;';
    复制代码


  • 当你要对一个DOM元素做出很多修改时,可以先进行一些‘预处理’,批量修改后再替换原始的元素

    • 创建一个副本(cloneNode()),对这个副本进行更新,然后替代原来的节点
      1. // slower - multiple reflows
      2. var list = ['foo', 'bar', 'baz'], elem, contents;
      3. for (var i = 0; i < list.length; i++) {
      4.     elem = document.createElement('div');
      5.     content = document.createTextNode(list[i]);
      6.     elem.appendChild(content);
      7.     document.body.appendChild(elem); // multiple reflows
      8. }
      9.             
      10. // faster - create a copy
      11. var orig = document.getElementById('container'),
      12.     clone = orig.cloneNode(true), // create a copy
      13.     list = ['foo', 'bar', 'baz'], elem, contents;
      14. clone.setAttribute('width', '50%');
      复制代码
    • 修改一个不可见的元素,可以先让其不可见(display:none),修改完成后,再恢复其可见(display:block),这样就会减少reflow的次数
      1. // slower
      2. var subElem = document.createElement('div'),
      3.     elem = document.getElementById('animated');
      4. elem.appendChild(subElem);
      5. elem.style.width = '320px';
      6.             
      7. // faster
      8. var subElem = document.createElement('div'),
      9.     elem = document.getElementById('animated');
      10. elem.style.display = 'none'; // will not be repainted
      11. elem.appendChild(subElem);
      12. elem.style.width = '320px';
      13. elem.style.display = 'block';
      复制代码
    • 创建一个文档片段(使用DocumentFragment()),修改完成后,再把它追加到原始文档中
      1. // slower
      2. var list = ['foo', 'bar', 'baz'], elem, contents;
      3. for (var i = 0; i < list.length; i++) {
      4.     elem = document.createElement('div');
      5.     content = document.createTextNode(list[i]);
      6.     elem.appendChild(content);
      7.     document.body.appendChild(elem); // multiple reflows
      8. }
      9.             
      10. // faster
      11. var fragment = document.createDocumentFragment(),
      12.     list = ['foo', 'bar', 'baz'], elem, contents;
      13. for (var i = 0; i < list.length; i++) {
      14.     elem = document.createElement('div');
      15.     content = document.createTextNode(list[i]);
      16.     fragment.appendChild(content);
      17. }
      18. document.body.appendChild(fragment); // one reflow
      复制代码

 

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册