简体   繁体   中英

Are d3's reusable components objects?

When we call

var xAxis = d3.svg.axis()

are we instantiating a new axis object? I understand the axis component is implemented as a closure, but I am confused if it is also an object.

My question also applies to Mike's article Towards Reusable Charts, specifically the end of this section . Using his pattern, if we do something like

var myChart = chart().width(720).height(80);

is myChart an object? If not, what is it? And what's the difference between doing this and doing var myChart = new chart(); ?

Yes, we're instantiating a new axis Object each time. This instance is a function , which in JavaScript is a first-class Object; meaning, you can assign properties to it like so:

function myFunc() {}
myFunc.foo = "bar";

myFunc();// This is possible (naturally)
console.log(myFunc.foo);// ...and this is valid too

If you wrapped the above code in a function:

function giveMeMyFunc() {
    function myFunc() {}
    return myFunc;
}

then every time you call

myFuncInstance = giveMeMyFunc();

you get a new instance of myFunc (which is also an Object), because myFunc is declared once per call.

So we've established that a function is also an Object. And, when a function returns another function it's as if it's returning a new instance of an Object, but being also a function, you could still call myFuncInstance() .

To drive the point home, and to perhaps answer your other questions, we can look at how d3.svg.axis() is actually implemented (loosely excerpted from the d3 source code):

d3.svg.axis = function() {
  /* Some variables here, which essentially are instance properties (protected through closure) */
  var scale = 123;
  ...

  /* This is a function, but since JavaScript functions are first-class objects, it's essentially an instance. */
  /* Each time (the outer) `d3.svg.axis()` is called, (the inner) `axis` function is a unique – not a shared – object. */
  function axis() {
    /* This is where the work of drawing the axis takes place, but won't get
      called until the axis is used (see below). */
  }

  /* Since the inner function `axis` is also an object, the following is an instance method */
  axis.scale = function(x) {
    scale = x;// here we're setting `scale`, which is basically an instance property

    // returning `axis` – a.k.a. our instance – is what enables method chaining: myAxis.scale(5).orient("left")
    return axis;
  }

  /* More methods here, like `axis.scale` above */

  /* Last line is very important: */
  /* This is where the newly created instance is return. Remember from */
  /* above, `axis` is a function, but it's an Object too, and it has the */
  /* methods we've just applied to it. */
  return axis;
}


/* Given all that, the line below returns an instance of `axis` (the inner function),
  which has more methods applied to it. */
myAxis = d3.svg.axis();

Finally, since the instance myAxis is also a function, you can call it. That's what d3 does when you apply an axis to a selection:

d3.select('.x_axis').call(myAxis);

D3 will call the myAxis function whose body, which is defined above in function axis() {} will do all the work of actually drawing some SVG stuff inside the element that matches the '.x_axis' selector.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM