0

I recently found a document by Mike Bostock, Towards Reusable Charts, which describes some conventions for encapsulating reusable charts in D3. Note however that the question isn't just about D3 but rather creating methods for JavaScript objects dynamically.

His proposal includes the following snippet:

function chart() {
  var width = 720, // default width
      height = 80; // default height

  function my() {
    // generate chart here, using `width` and `height`
  }

  my.width = function(value) {
    if (!arguments.length) return width;
    width = value;
    return my;
  };

  my.height = function(value) {
    if (!arguments.length) return height;
    height = value;
    return my;
  };

  return my;
}

This is all fine but what I am immediately wondering once I started adding more chart properties than just width and height to it is whether the common parts of these "property accessor functions" could be factored out somehow.

I am imagining some method to dynamically register a property like this:

my.addProperty(propName) {
  my[propName] = function (value) {
                   if (!arguments.length) return getPrivateVar(propName);
                   setPrivateVar(propName, value);
                   return my;
                 };
}

So I could then simply do

my.addProperty('width');
my.addProperty('height');

to achieve the same result as the my.width = and my.height = statements from the code above.

Is this even possible? How?

EDIT:

The crux of the matter is obviously accessing the "private" variables width and height of the chart() function by name (represented by the get/setPrivateVar() method calls inside my addProperty idea). Therefore, answers to this related question (which focus on getting a global variable by name) obviously won't work.

Community
  • 1
  • 1
FriendFX
  • 2,929
  • 1
  • 34
  • 63

1 Answers1

0

This could solve your problem:

function chart() {
  var options = {
      width : 720, // default width
      height : 80 // default height
  };

  var my = function() {

  };

  var accessorFunction = function(object, propertyName) {
    return function(value) {
      if (!value) return options[propertyName];
      options[propertyName] = value;
      return object;
    };
  };

  for (propertyName in options) {
    my[propertyName] = accessorFunction(my, propertyName);
  }

  return my;
}

console.log(chart().width());
console.log(chart().width(721).width());
Davide Ungari
  • 1,920
  • 1
  • 13
  • 24
  • Great, thanks. Your `propertyNames in options` loop is a nice additional benefit of your solution. Although I would have preferred to keep the `width` and `height` variables as plain `var`s inside `chart`, I believe this is currently impossible. – FriendFX Jan 07 '16 at 22:48