Reliable JavaScript. Lawrence Spencer

Чтение книги онлайн.

Читать онлайн книгу Reliable JavaScript - Lawrence Spencer страница 7

Reliable JavaScript - Lawrence Spencer

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

industrial-strength JavaScript, get used to functions being first-class objects that are passed as arguments, sent back as return values and just about anything else you can imagine. As first-class citizens of JavaScript, they can even have properties and methods of their own.

      NOTE In JavaScript, functions are objects that can have methods and properties. Your functions can have more flexibility and power than they might in other languages.

      You can see an example of attaching a method to a function in this part of Listing 1-1:

      It creates a function, x, that is a member of the returned function, line. Shortly, you will see how x and its twin, y, are used, and learn the very JavaScript-ey peculiarities of what’s inside them.

      So the call rj3.svg.line() returns a function. Continuing with Listing 1-2, the function is called with arrayData, which becomes the data argument to that inner line function from Listing 1-1. From there, the while loop fills the points array from the incoming data:

      Each element of data, held in the variable d, is passed to the getX and getY functions, which extract the x and y coordinates. (The use of call to invoke getX and getY will be covered at the end of this Case Study, as well as in Chapter 18. The + in front of getX and getY is a little trick to ensure that actual numbers, not numeric strings, go in the points array.) By default, those coordinates are the first and second elements of the 2-element array that comprises each element of arrayData. This occurs in the following snippet of Listing 1-1.

      Next, the segment function is called. This is a function at yet another level of nesting, private to the line function. It fills the segments variable, putting the SVG "M" command in the first element and the path in the second. From Listing 1-1 again:

      The path is produced by the interpolate function, which in the default implementation just joins the points (each implicitly converted to a string), putting an "L" between them. (We’ll cover interpolate in more detail later in this chapter.)

      Thus, the array

      becomes

      As a final step, the two elements of segments ("M" and the points-as-string) are joined in the return statement to produce the SVG path

      That’s the basic operation. Now for some complications that will illustrate additional ways that you can use JavaScript idiomatically.

      Suppose that each point in your data were an object instead of an [x,y] coordinate pair in array form. It might look something like this:

      How could you use rj3.svg.line to draw it? One way would be to transform the data on the way in, as in Listing 1-3.

      LISTING 1-3: Transforming the data on the way in (code filename: rj3\pathFromTransformedObjects.js)

      However, that would be wasteful, as it creates a second, complete copy of the data. It’s the sort of thing a C# programmer accustomed to the efficiencies of LINQ would do. (LINQ peels off just one element at a time from an array as requested, without making a second copy of the whole array.)

      The strategy in Listing 1-3 would also limit your possibilities in the user interface. You probably want your line to change dynamically if the data change. Thanks to the design decision that you’re going to see in a moment, D3 does this for you with no effort – but only with the data it knows about. If you have called its functions with only a one-time copy of the real data, you don’t get this benefit.

      The design decision is exemplified by the little functions, line.x and line.y. Listing 1-4 shows how to use them.

      LISTING 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

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