0

I am trying to learn D3 and i am having a hard time to understand the anonymous functions.

In the following example, there is at the bottom function(f) and it returns returns the variable "interpolate" with the parameter f (which i think is itself akward, since "interpolate" is not a function, but a variable).

But what is the meaning of F? I do not see how or where it is used in the function of "interpolate". If i delete F and just pass (), my animation breaks.

Thanks! :)

svg.append('path')
        .attr('class', 'line')
        .attr('d', line(lineData))
        .transition()
        .duration(3000)
        .attrTween('d', pathTween);

    function pathTween() {
        var interpolate = d3.scale.quantile()
                .domain([0,1])
                .range(d3.range(0, 7));
        return function(f) {
            return line(lineData.slice(0, interpolate(f)));
        };
    }
mangotasche
  • 731
  • 2
  • 7
  • 14
  • Your `pathTween()` creates a function which is sent to d3. At some point d3 then decides to call your function and pass it one parameter - `f`. – Sirko Feb 17 '17 at 10:05
  • 1
    Not strictly related to D3, but Js in general, you might want to take a look at [what a closure is](http://stackoverflow.com/questions/111102/how-do-javascript-closures-work) and how it works. Make sure you understand what happens when a function returns another function that accepts a parameter., like in this case. – Aurelio Feb 17 '17 at 10:11
  • @Nobita It is specific to `D3`, as it all boils down to the fact that the `d3.scale.quantile` returns a function, for which `f` will be between 0 and 1 for the duration of the animation. That `interpolate` variable's then gonna convert numbers to an appropriate range. – Ian Feb 17 '17 at 12:28
  • @Ian "Not strictly related to D3" was referring to the fact that in case the OP does not know what happens in a closure - which we have here - it could be a good (actually it is a an essential) pre-requisite to understand the d3 implementation. – Aurelio Feb 17 '17 at 14:09

2 Answers2

3

Let's break this down:

....
.attrTween('d', pathTween);

function pathTween() {
    var interpolate = d3.scale.quantile()
            .domain([0,1])
            .range(d3.range(0, 7));
    return function(f) {
        return line(lineData.slice(0, interpolate(f)));
    };
}
  1. We are passing the closure function pathTween to attrTween. Under the hood d3 will invoke pathTween for every element in your selection (in your case it's just one path).
  2. d3 is expecting the function passed to attrTween to return a function. This is the anonymous function which takes parameter f (most d3 examples will use variable t, more on that in a second).
  3. The pathTween function is a closure because it creates the inner variable interpolate and closes it so that it's available in the inner returned function. You stated it returns the variable "interpolate" with the parameter f (which i think is itself awkward, since "interpolate" is not a function but a variable). This is not true; first, we don't return interpolate at all (but it is available in the closure ) and second, it is a function that is being stored in a variable. Storing functions in variables is very common in JavaScript (and a lot of other programming languages too).
  4. Now that d3 has the inner anon function, it's going to call it for every tick of the animation (usually every 16ms). When it calls it, its going to pass in f. What's f? It's a timer (why it's usually called t). It'll contain a value from 0 to 1 that indicates where it is in the transition (0 is start, 1 in end). This variable is then passed to the function interpolate, which happens to be expecting a value between 0 and 1 (see domain[0,1]).

Check this out:

var interpolate = d3.scale.quantile()
                .domain([0,1])
                .range(d3.range(0, 7));
                
console.log(typeof(interpolate));

console.log(interpolate(0));
console.log(interpolate(0.25));
console.log(interpolate(0.5));
console.log(interpolate(1));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Mark
  • 106,305
  • 20
  • 172
  • 230
1

In javascript, functions can be passed around in a variable! https://en.wikipedia.org/wiki/First-class_function

var interpolate = d3.scale.quantile()
                .domain([0,1])
                .range(d3.range(0, 7));

in this line, D3 creates a new function for you and it is then assigned to the variable interpolate ready to be executed at a later time

return function(f) {
            return line(lineData.slice(0, interpolate(f)));
        };

This then returns a function that can be called by D3. When D3 calls this function it passes in a value for f which your interpolate function can use as input. Removing f would mean the value undefined is passed to the function instead.

dpix
  • 2,765
  • 2
  • 16
  • 25
  • You don't actually explain what the parameter `f` is though, which I'm assuming what the question is asking. – Ian Feb 17 '17 at 12:26
  • Sorry, it's been a while since I used D3. Thought it was still worthwhile as OP seemed like they were most confused about passing functions around. Accepted answer is much better than mine _b – dpix Feb 17 '17 at 14:15