Скачать книгу

1-4: Using line.x and line.y (code filename: rj3\pathFromObjects.js)

      The call

      replaces the default value of Listing 1-1’s getX variable with your new function. Now, when the while loop calls

      the getX.call will invoke your function, which returns the x property of your objects – the original, authoritative objects, and not copies of them.

      There’s something else worth noting about those calls. Without stealing all the thunder from Chapter 18, we’ll state that whatever function is installed to get the x coordinate is actually called with two arguments, even though your function(d){return d.x;} only took one. The second argument, i, is the index of the datum, d, in the array. You didn’t use i, but you could have. This is how the object-oriented concept of function overloading works in JavaScript.

      Another example of JavaScript’s function overloading is in the line.x function itself. Did you notice the if test of arguments?

      In JavaScript, arguments is an array-like object that is available inside every function, containing the arguments the function was called with. Here, the test inspects the length of that pseudo-array. Zero is a “falsy” value in JavaScript (see “Values May Be Truthy or Falsy” in Chapter 25) so if there are no arguments, the function just returns the current value of getX.

      To recap, if line.x is called with no arguments, it returns the current accessor for x-coordinates. If it is called with an argument, it sets the x-coordinate accessor to it and returns something else entirely, namely the line function-object. This, and the possibility of the extra argument, i, exemplify function overloading in JavaScript.

      NOTE In JavaScript, the object-oriented concept of function overloading is done by inspecting the function’s arguments and adjusting accordingly.

      Now why would a function that sets the x-accessor return the line? You probably know the answer: It allows you to chain the calls as you saw in Listing 1-4:

      The design possibilities of call-chaining are explored at length in Chapter 15.

      Now here’s a question for you. What do you suppose would happen if you were to add a z-coordinate to each data point?

      If you guessed that the program would happily produce exactly the same result, you are right. In JavaScript, an object with x, y, and z properties can also function as an object with x and y properties.

      You could also produce the objects with a constructor function, which looks completely different but has the same result:

      This is called duck typing, after the saying, “If it looks like a duck, walks like a duck and quacks like a duck, it is a duck.” In JavaScript, ducks are some of your best friends. It is possible to distinguish the cases thus:

      However, there is almost never a reason to do so. A C# or Java programmer might attempt to learn whether an object is up to snuff through such inspections, but the JavaScript way is to simply check for the existence of the properties:

      or

      Duck typing is not sloppiness. It is an important way to give a component more reach.

      NOTE Embrace duck typing. It allows a little code to accommodate a wide range of objects.

      If you read Listing 1-1 with unusual attention, you might have wondered how the inner line function manages to access the private variables of the outer rj3.svg.line after the outer function has returned. Programmers from other languages might expect the variables getX, getY, and interpolate to pop off the stack once control exits the function that declared them. And so they would, except for one thing: JavaScript’s concept of closures.

      We said earlier that when you call rj3.svg.line(), it returns the inner line function. There's more to it than that. It actually returns a closure, which you can think of as an object that from the outside looks like the function (inner line), but on the inside also remembers the environment that prevailed when the function was created (the variables getX, getY and interpolate). You call inner line’s functions as you normally would, but they are aware of line’s original environment.

      NOTE Closures are a very powerful design element in JavaScript. Every function is a closure.

      Consider once more the call statements in the while loop:

      What does getX.call(this,d,i) really do? In English, it calls the getX function, pretending that it is a member of the object this (more on that in a moment) and passing the arguments d and i. The special variable this is, loosely speaking, the “object before the dot” when you call the function in which this appears.

      Why all this fuss and bother? Why not just say getX(d,i) and be done with it? In JavaScript, the ability to specify this is an important design opportunity.

      NOTE In JavaScript, “ this ” offers a design opportunity. Use it!

      Listing 1-5 shows the power of this language feature. Here, the data are just an array of years. The function line.x computes the desired x coordinate based on the index, i (now we’re using i!), but what’s going on with line.y? It appears to be calling a function, getValue, that is nowhere in scope.

      LISTING 1-5: Extending the line generator to get values from an outer object (code filename rj3\pathFromFunction.js)

      So where does getValue come from? In the second part of the listing, a yearlyPriceGrapher object is instantiated that combines a line generator with a function, getValue, that returns the value for a given year. In the call

the yearlyPriceGrapher is “dotted with” lineGenerator. That means that yearlyPriceGrapher becomes this in the y-accessor, which causes its getValue to be invoked properly. The result is in Figure 1.4.

Figure 1.4

      It is natural to think that this refers to the function in which it appears, or maybe the object enclosing the function. Not so. It refers to the object on which the function is called.

      JavaScript Is Single-Threaded

      Just one more thing to close out this section about language features: JavaScript is single-threaded. That doesn’t mean it uses a blocking model – far from it. It just means that you do asynchronous programming differently.

      Where a multi-threaded language would allow you to start a task that runs in parallel to the code that spawned it, in JavaScript you

Скачать книгу