[翻译]High Performance JavaScript(030)

第十章  Tools  工具


    Having the right software is essential for identifying bottlenecks in both the loading and running of scripts. A number of browser vendors and large-scale websites have shared techniques and tools to help make the Web faster and more efficient. This chapter focuses on some of the free tools available for:



Profiling  性能分析

    Timing various functions and operations during script execution to identify areas for optimization



Network analysis  网络分析
    Examining the loading of images, stylesheets, and scripts and their effect on overall page load and rendering



    When a particular script or application is performing less than optimally, a profiler can help prioritize areas for optimization. This can get tricky because of the range of supported browsers, but many vendors now provide a profiler along with their debugging tools. In some cases, performance issues may be specific to a particular browser; other times, the symptoms may occur across multiple browsers. Keep in mind that the optimizations applied to one browser might benefit other browsers, but they might have the opposite effect as well. Rather than assuming which functions or operations are slow, profilers ensure that optimization time is spent on the slowest areas of the system that affect the most browsers.



    While the bulk of this chapter focuses on profiling tools, network analyzers can be highly effective in helping to ensure that scripts and pages are loading and running as quickly as possible. Before diving into tweaking code, you should be sure that all scripts and other assets are being loaded optimally. Image and stylesheet loading can affect the loading of scripts, depending on how many concurrent requests the browser allows and how many assets are being loaded.



    Some of these tools provide tips on how to improve the performance of web pages. Keep in mind that the best way to interpret the information these tools provide is to learn more about the rationale behind the rules. As with most rules, there are exceptions, and a deeper understanding of the rules allows you to know when to break them.



JavaScript Profiling  JavaScript性能分析


    The tool that comes with all JavaScript implementations is the language itself. Using the Date object, a measurement can be taken at any given point in a script. Before other tools existed, this was a common way to time script execution, and it is still occasionally useful. By default the Date object returns the current time, and subtracting one Date instance from another gives the elapsed time in milliseconds. Consider the following example, which compares creating elements from scratch with cloning from an existing element (see Chapter 3, DOM Scripting):



var start = new Date(),
    count = 10000,
    i, element, time;

for (i = 0; i < count; i++) {
  element = document.createElement('div');

time = new Date() - start;
alert('created ' + count + ' in ' + time + 'ms');

start = new Date();
for (i = 0, i < count; i++) {
  element = element.cloneNode(false);

time = new Date() - start;
alert('created ' + count + ' in ' + time + 'ms');


    This type of profiling is cumbersome to implement, as it requires manually instrumenting your own timing code. A Timer object that handles the time calculations and stores the data would be a good next step.



Var Timer = {
  _data: {},

  start: function(key) {
    Timer._data[key] = new Date();

  stop: function(key) {
    var time = Timer._data[key];
    if (time) {
      Timer._data[key] = new Date() - time;

  getTime: function(key) {
    return Timer._data[key];

for (i = 0; i < count; i++) {
  element = document.createElement('div');

alert('created ' + count + ' in ' + Timer.getTime('createElement');

    As you can see, this still requires manual instrumentation, but provides a pattern for building a pure JavaScript profiler. By extending the Timer object concept, a profiler can be constructed that registers functions and instruments them with timing code.



YUI Profiler  YUI分析器


    The YUI Profiler (http://developer.yahoo.com/yui/profiler/), contributed by Nicholas Zakas, is a JavaScript profiler written in JavaScript. In addition to timer functionality, it provides interfaces for profiling functions, objects, and constructors, as well as detailed reports of the profile data. It enables profiling across various browsers and data exporting for more robust reporting and analysis.

    YUI分析器(http://developer.yahoo.com/yui/profiler/),由Nicholas Zakas提供,是用JavaScript编写的JavaScript分析器。除了计时功能,它还提供了用于函数、对象、和构造器的性能分析接口,还包括性能分析数据的详细报告。它可以跨浏览器工作,其输出数据可提供更强大的报告和分析。


    The YUI Profiler provides a generic timer that collects performance data. Profiler provides static methods for starting and stopping named timings and retrieving profile data.



var count = 10000, i, element;

for (i = 0; i < count; i++) {
  element = document.createElement('div');

alert('created ' + count + ' in ' + Y.Profiler.getAverage('createElement') + 'ms');

    This clearly improves upon the inline Date and Timer approach and provides additional profile data regarding the number of times called, as well as the average, minimum, and maximum times. This data is collected and can be analyzed alongside other profile results.



    Functions can be registered for profiling as well. The registered function is instrumented with code that collects performance data. For example, to profile the global initUI method from Chapter 2, all that is required is the name:




    Many functions are bound to objects in order to prevent pollution of the global namespace. Object methods can be registered by passing the object in as the second argument to registerFunction. For example, assume an object called uiTest that implements two initUI approaches as uiTest.test1 and uiTest.test2. Each can be registered individually:



Y.Profiler.registerFunction("test1", uiTest);
Y.Profiler.registerFunction("test2", uiTest);

    This works well enough, but doesn't really scale for profiling many functions or an entire application. The registerObject method automatically registers every method bound to the object:



Y.Profiler.registerObject("uiTest", uiTest); 

    The first argument is the name of the object (for reporting purposes), and the second is the object itself. This will instrument profiling for all of the uiTest methods.



    Objects that rely on prototype inheritance need special handling. YUI's profiler allows the registration of a constructor function that will instrument all methods on all instances of the object:



Y.Profiler.registerConstructor("MyWidget", myNameSpace);

    Now every function on each instance of myNameSpace.MyWidget will be measured and reported on. An individual report can be retrieved as an object:



var initUIReport = Y.Profiler.getReport("initUI");

    This provides an object containing the profile data, including an array of points, which are the timings for each call, in the order they were called. These points can be plotted and analyzed in other interesting ways to examine the variations in time. This object has the following fields:



  min: 100,
  max: 250,
  calls: 5,
  avg: 120,
  points: [100, 200, 250, 110, 100]

    Sometimes you may want only the value of a particular field. Static Profiler methods provide discrete data per function or method:



var uiTest1Report = {
  calls: Y.Profiler.getCalls("uiTest.test1"),
  avg: Y.Profiler.getAvg("uiTest.test1")

    A view that highlights the slowest areas of the code is really what is needed in order to properly analyze a script's performance. A report of all registered functions called on the object or constructor is also available:



var uiTestReport = Y.Profiler.getReport("uiTest");

    This returns an object with the following data:



  test1: {
    min: 100,
    max: 250,
    calls: 10,
    avg: 120
    min: 80,
    max: 210,
    calls: 10,
    avg: 90

    This provides the opportunity to sort and view the data in more meaningful ways, allowing the slower areas of the code to be scrutinized more closely. A full report of all of the current profile data can also be generated. This, however, may contain useless information, such as functions that were called zero times or that are already meeting performance expectations. In order to minimize this type of noise, an optional function can be passed in to filter the data:



var fullReport = Y.Profiler.getFullReport(function(data) {
  return (data.calls > 0 && data.avg > 5);

    The Boolean value returned will indicate whether the function should be included in the report, allowing the less interesting data to be suppressed.



    When finished profiling, functions, objects, and constructors can be unregistered individually, clearing the profile data:




    The clear() method keeps the current profile registry but clears the associated data. This function can be called individually per function or timing:




    Or all data may be cleared at once by omitting the name argument:




    Because it is in JSON format, the profile report data can be viewed in any number of ways. The simplest way to view it is on a web page by outputting as HTML. It can also be sent to a server, where it can be stored in a database for more robust reporting. This is especially useful when comparing various optimization techniques across browsers.



    It is worth noting that anonymous functions are especially troublesome for this type of profiler because there is no name to report with. The YUI Profiler provides a mechanism for instrumenting anonymous functions, allowing them to be profiled. Registering an anonymous function returns a wrapper function that can be called instead of the anonymous function:



var instrumentedFunction =
  Y.Profiler.instrument("anonymous1", function(num1, num2){
    return num1 + num2;
instrumentedFunction(3, 5);

    This adds the data for the anonymous function to the Profiler's result set, allowing it to be retrieved in the same manner as other profile data:



var report = Y.Profiler.getReport("anonymous1");


Anonymous Functions  匿名函数


    Depending on the profiler, some data can be obscured by the use of anonymous functions or function assignments. As this is a common pattern in JavaScript, many of the functions being profiled may be anonymous, making it difficult or impossible to measure and analyze. The best way to enable profiling of anonymous functions is to name them. Using pointers to object methods rather than closures will allow the broadest possible profile coverage.



    Compare using an inline function:



myNode.onclick = function() {

    with a method call:



myApp._onClick = function() {
myNode.onclick = myApp._onClick;

    Using the method call allows any of the reviewed profilers to automatically instrument the onclick handler. This is not always practical, as it may require significant refactoring in order to enable profiling.



    For profilers that automatically instrument anonymous functions, adding an inline name makes the reports more readable:



myNode.onclick = function myNodeClickHandler() {

    This also works with functions declared as variables, which some profilers have trouble gleaning a name from:



var onClick = function myNodeClickHandler() {

    The anonymous function is now named, providing most profilers with something meaningful to display along with the profile results. These names require little effort to implement, and can even be inserted automatically as part of a debug build process.


个人分类: 计算机技术
上一篇[翻译]High Performance JavaScript(029)
下一篇[翻译]High Performance JavaScript(031)
想对作者说点什么? 我来说一句