第八章 Programming Practices 编程实践

第八章 Programming Practices 编程实践

每种编程语言都有痛点,而且低效模式随着时间的推移不断发展。其原因在于,越来越多的人们开始使用这种语言,不断扩种它的边界。自 2005 年以来,当术语“Ajax”出现时,网页开发者对 JavaScript 和浏览器的推动作用远超过以往。其结果是出现了一些非常具体的模式,即有优秀的做法也有糟糕的做法。这些模式的出现,是因为网络上 JavaScript 的性质决定的。

  • JavaScript与许多脚本语言一样,允许你在程序中获取一个包含代码的字符串然后运行它。有四种标准方法可以实现: eval_r(), Function()构造器, setTimeout()和 setInterval()。每个函数允许你传入一串 JavaScript代码,然后运行它。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var num1 = 5,
    num2 = 6,
    //eval_r() evaluating a string of code
    result = eval_r("num1 + num2"),
    //Function() evaluating strings of code
    sum = new Function("arg1", "arg2", "return arg1 + arg2");
    //setTimeout() evaluating a string of code
    setTimeout("sum = num1 + num2", 100);
    //setInterval() evaluating a string of code
    setInterval("sum = num1 + num2", 100);

    大多数情况下,没必要使用 eval_r()或 Function(),如果可能的话,尽量避免使用它们。至于另外两个函数,setTimeout()和 setInterval(),第一个参数传入一个函数而不是一个字符串。例如:

    1
    2
    3
    4
    5
    6
    setTimeout(function(){
    sum = num1 + num2;
    }, 100);
    setInterval(function(){
    sum = num1 + num2;
    }, 100);
  • 尽量使用对象直接量。这样可以提高性能,同时减少代码量。

  • 不要做重复的工作。下面例子解决重复工作的问题。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function addHandler(target, eventType, handler){
    if (target.addEventListener){ //DOM2 Events
    target.addEventListener(eventType, handler, false);
    } else { //IE
    target.attachEvent("on" + eventType, handler);
    }
    }
    function removeHandler(target, eventType, handler){
    if (target.removeEventListener){ //DOM2 Events
    target.removeEventListener(eventType, handler, false);
    } else { //IE
    target.detachEvent("on" + eventType, handler);
    }
    }

    隐藏的性能问题在于每次函数调用时都执行重复工作。每一次,都进行同样的检查,看看某种方法是否存在。如果你假设 target 唯一的值就是 DOM 对象,而且用户不可能在页面加载时魔术般地改变浏览器,那么这种判断就是重复的。如果 addHandler()一上来就调用addEventListener()那么每个后续调用都要出现这句代码。在每次调用中重复同样的工作是一种浪费,有多种办法避免这一点。

    可以使用延迟加载:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    function addHandler(target, eventType, handler){
    //overwrite the existing function
    if (target.addEventListener){ //DOM2 Events
    addHandler = function(target, eventType, handler){
    target.addEventListener(eventType, handler, false);
    };
    } else { //IE
    addHandler = function(target, eventType, handler){
    target.attachEvent("on" + eventType, handler);
    };
    }
    //call the new function
    addHandler(target, eventType, handler);
    }
    function removeHandler(target, eventType, handler){
    //overwrite the existing function
    if (target.removeEventListener){ //DOM2 Events
    removeHandler = function(target, eventType, handler){
    target.addEventListener(eventType, handler, false);
    };
    } else { //IE
    removeHandler = function(target, eventType, handler){
    target.detachEvent("on" + eventType, handler);
    };
    }
    //call the new function
    removeHandler(target, eventType, handler);
    }

    条件预加载:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    var addHandler = document.body.addEventListener ?
    function(target, eventType, handler){
    target.addEventListener(eventType, handler, false);
    }:
    function(target, eventType, handler){
    target.attachEvent("on" + eventType, handler);
    };
    var removeHandler = document.body.removeEventListener ?
    function(target, eventType, handler){
    target.removeEventListener(eventType, handler, false);
    }:
    function(target, eventType, handler){
    target.detachEvent("on" + eventType, handler);
    };
  • 使用位操作符可以提升性能。

  • 尽量使用原生方法,可以提高性能。如Math.abs(num)等。

Summary 总结

JavaScript 提出了一些独特的性能挑战,关系到你组织代码的方法。网页应用变得越来越高级,包含的
JavaScript 代码越来越多,出现了一些模式和反模式。请牢记以下编程经验:

  • 通过避免使用 eval_r()和 Function()构造器避免二次评估。此外,给 setTimeout()和 setInterval()传递函数参数而不是字符串参数。
  • 创建新对象和数组时使用对象直接量和数组直接量。它们比非直接量形式创建和初始化更快。
  • 避免重复进行相同工作。当需要检测浏览器时,使用延迟加载或条件预加载。
  • 当执行数学远算时,考虑使用位操作,它直接在数字底层进行操作。
  • 原生方法总是比 JavaScript 写的东西要快。尽量使用原生方法。
  • 本书涵盖了很多技术和方法,如果将这些优化应用在那些经常运行的代码上,你将会看到巨大的性能提升。