You are previewing JavaScript: The Good Parts.

JavaScript: The Good Parts

Cover of JavaScript: The Good Parts by Douglas Crockford Published by O'Reilly Media, Inc.
  1. JavaScript: The Good Parts
    1. SPECIAL OFFER: Upgrade this ebook with O’Reilly
    2. A Note Regarding Supplemental Files
    3. Preface
      1. Conventions Used in This Book
      2. Using Code Examples
      3. Safari® Books Online
      4. How to Contact Us
      5. Acknowledgments
    4. 1. Good Parts
      1. Why JavaScript?
      2. Analyzing JavaScript
      3. A Simple Testing Ground
    5. 2. Grammar
      1. Whitespace
      2. Names
      3. Numbers
      4. Strings
      5. Statements
      6. Expressions
      7. Literals
      8. Functions
    6. 3. Objects
      1. Object Literals
      2. Retrieval
      3. Update
      4. Reference
      5. Prototype
      6. Reflection
      7. Enumeration
      8. Delete
      9. Global Abatement
    7. 4. Functions
      1. Function Objects
      2. Function Literal
      3. Invocation
      4. Arguments
      5. Return
      6. Exceptions
      7. Augmenting Types
      8. Recursion
      9. Scope
      10. Closure
      11. Callbacks
      12. Module
      13. Cascade
      14. Curry
      15. Memoization
    8. 5. Inheritance
      1. Pseudoclassical
      2. Object Specifiers
      3. Prototypal
      4. Functional
      5. Parts
    9. 6. Arrays
      1. Array Literals
      2. Length
      3. Delete
      4. Enumeration
      5. Confusion
      6. Methods
      7. Dimensions
    10. 7. Regular Expressions
      1. An Example
      2. Construction
      3. Elements
    11. 8. Methods
    12. 9. Style
    13. 10. Beautiful Features
    14. A. Awful Parts
      1. Global Variables
      2. Scope
      3. Semicolon Insertion
      4. Reserved Words
      5. Unicode
      6. typeof
      7. parseInt
      8. +
      9. Floating Point
      10. NaN
      11. Phony Arrays
      12. Falsy Values
      13. hasOwnProperty
      14. Object
    15. B. Bad Parts
      1. ==
      2. with Statement
      3. eval
      4. continue Statement
      5. switch Fall Through
      6. Block-less Statements
      7. ++ −−
      8. Bitwise Operators
      9. The function Statement Versus the function Expression
      10. Typed Wrappers
      11. new
      12. void
    16. C. JSLint
      1. Undefined Variables and Functions
      2. Members
      3. Options
      4. Semicolon
      5. Line Breaking
      6. Comma
      7. Required Blocks
      8. Forbidden Blocks
      9. Expression Statements
      10. for in Statement
      11. switch Statement
      12. var Statement
      13. with Statement
      14. =
      15. == and !=
      16. Labels
      17. Unreachable Code
      18. Confusing Pluses and Minuses
      19. ++ and −−
      20. Bitwise Operators
      21. eval Is Evil
      22. void
      23. Regular Expressions
      24. Constructors and new
      25. Not Looked For
      26. HTML
      27. JSON
      28. Report
    17. D. Syntax Diagrams
    18. E. JSON
      1. JSON Syntax
      2. Using JSON Securely
      3. A JSON Parser
    19. Index
    20. About the Author
    21. Colophon
    22. SPECIAL OFFER: Upgrade this ebook with O’Reilly
O'Reilly logo

Closure

The good news about scope is that inner functions get access to the parameters and variables of the functions they are defined within (with the exception of this and arguments). This is a very good thing.

Our getElementsByAttribute function worked because it declared a results variable, and the inner function that it passed to walk_the_DOM also had access to the results variable.

A more interesting case is when the inner function has a longer lifetime than its outer function.

Earlier, we made a myObject that had a value and an increment method. Suppose we wanted to protect the value from unauthorized changes.

Instead of initializing myObject with an object literal, we will initialize myObject by calling a function that returns an object literal. That function defines a value variable. That variable is always available to the increment and getValue methods, but the function's scope keeps it hidden from the rest of the program:

var myObject = (function () {
    var value = 0;

    return {
        increment: function (inc) {
            value += typeof inc === 'number' ? inc : 1;
        },
        getValue: function (  ) {
            return value;
        }
    };
}());

We are not assigning a function to myObject. We are assigning the result of invoking that function. Notice the ( ) on the last line. The function returns an object containing two methods, and those methods continue to enjoy the privilege of access to the value variable.

The Quo constructor from earlier in this chapter produced an object with a status property and a get_status method. But that doesn't seem very interesting. Why would you call a getter method on a property you could access directly? It would be more useful if the status property were private. So, let's define a different kind of quo function to do that:

// Create a maker function called quo. It makes an
// object with a get_status method and a private
// status property.

var quo = function (status) {
    return {
        get_status: function (  ) {
            return status;
        }
    };
};

// Make an instance of quo.

var myQuo = quo("amazed");

document.writeln(myQuo.get_status(  ));

This quo function is designed to be used without the new prefix, so the name is not capitalized. When we call quo, it returns a new object containing a get_status method. A reference to that object is stored in myQuo. The get_status method still has privileged access to quo's status property even though quo has already returned. get_status does not have access to a copy of the parameter; it has access to the parameter itself. This is possible because the function has access to the context in which it was created. This is called closure.

Let's look at a more useful example:

// Define a function that sets a DOM node's color
// to yellow and then fades it to white.

var fade = function (node) {
    var level = 1;
    var step = function (  ) {
        var hex = level.toString(16);
        node.style.backgroundColor = '#FFFF' + hex + hex;
        if (level < 15) {
            level += 1;
            setTimeout(step, 100);
        }
    };
    setTimeout(step, 100);
};

fade(document.body);

We call fade, passing it document.body (the node created by the HTML <body> tag). fade sets level to 1. It defines a step function. It calls setTimeout, passing it the step function and a time (100 milliseconds). It then returns—fade has finished.

Suddenly, about a 10th of a second later, the step function gets invoked. It makes a base 16 character from fade's level. It then modifies the background color of fade's node. It then looks at fade's level. If it hasn't gotten to white yet, it then increments fade's level and uses setTimeout to schedule itself to run again.

Suddenly, the step function gets invoked again. But this time, fade 's level is 2. fade returned a while ago, but its variables continue to live as long as they are needed by one or more of fade's inner functions.

It is important to understand that the inner function has access to the actual variables of the outer functions and not copies in order to avoid the following problem.

// BAD EXAMPLE

// Make a function that assigns event handler functions to an array
 of nodes the wrong way.
// When you click on a node, an alert box is supposed to display the ordinal
of the node.
// But it always displays the number of nodes instead.

var add_the_handlers = function (nodes) {
    var i;
    for (i = 0; i < nodes.length; i += 1) {
        nodes[i].onclick = function (e) {
            alert(i);
        };
    }
};

// END BAD EXAMPLE

The add_the_handlers function was intended to give each handler a unique number i. It fails because the handler functions are bound to the variable i, not the value of the variable i at the time the function was made.

// BETTER EXAMPLE

// Make a function that assigns event handler functions to an array of nodes.
// When you click on a node, an alert box will display the ordinal of the node.

var add_the_handlers = function (nodes) {
    var helper = function (i) {
       return function (e) {
          alert(i);
       };
    };
    var i;
    for (i = 0; i < nodes.length; i += 1) {
        modes[i].onclick = helper(i);
    }
};

Avoid creating functions within a loop. It can be wasteful computationally,and it can cause confusion, as we saw with the bad example. We avoid the confusion by creating a helper function outside of the loop that will deliver a function that binds to the current value of i.

The best content for your career. Discover unlimited learning on demand for around $1/day.