- #1
- 2,163
- 3,834
This is going to be a long post. 
I have been teaching myself Javascript for a project that I'm working on and I am stuck on how to get inheritance working the way that I would like. If this was plain old Java, I would have been done days ago. Some background on the problem - I am building Javascript that uses the d3.js visualization libraries. I am building a tool that allows users to dynamically build various types of visualizations like bar charts, line charts, pie charts, etc. I have this working nicely but have gotten to the point where I'm seeing a lot of duplication that I would like to put into a base class that each of the implementations can use to store common variables and methods. Stripping out the bulk of the implementation details, the working bar chart looks like this:
In the calling code, this gets run in various ways like this:
Now comes the part that I'm stuck on. I would like to have a generic_chart class that each of the different types of charts are based on. Each section in the bar_chart function has code that could be generically used including the variables, things like chart.settings, and parts of the chart(settings) function. I have looked at various tutorials on inheritance and haven't found anything that really does what I would like. This is generally what I'm trying to do:
Then, when I call this:
// Setup
var barChart = bar_chart()
.settings(settings)
.y(function (d) { return +d; });
It would use the settings function in the generic_chart and the y function in the bar_chart. Unfortunately, this just isn't working and every time I get close, something else breaks. Help!

I have been teaching myself Javascript for a project that I'm working on and I am stuck on how to get inheritance working the way that I would like. If this was plain old Java, I would have been done days ago. Some background on the problem - I am building Javascript that uses the d3.js visualization libraries. I am building a tool that allows users to dynamically build various types of visualizations like bar charts, line charts, pie charts, etc. I have this working nicely but have gotten to the point where I'm seeing a lot of duplication that I would like to put into a base class that each of the implementations can use to store common variables and methods. Stripping out the bulk of the implementation details, the working bar chart looks like this:
JavaScript:
function bar_chart() {
var margin,
settings,
width,
height,
other variables...;
function chart(selection) {
// Initialization of variables
selection.each(function (data, i) {
// Code that assembles data based on the variables
);
//Code that builds the axes
}
chart.margin = function (_) {
if (!arguments.length) return margin;
margin = _;
return chart;
};
chart.settings = function (_) {
if (!arguments.length) return settings;
settings = _;
return chart;
};
chart.width = function (_) {
if (!arguments.length) return width;
width = _;
return chart;
};
chart.height = function (_) {
if (!arguments.length) return height;
height = _;
return chart;
};
chart.horizontal = function (_) {
if (!arguments.length) return horizontal ;
horizontal = _;
return chart;
};
chart.x = function (_) {
if (!arguments.length) return xValue;
xValue = _;
return chart;
};
chart.y = function (_) {
if (!arguments.length) return yValue;
yValue = _;
return chart;
};
return chart;
}
In the calling code, this gets run in various ways like this:
JavaScript:
// Setup
var barChart = bar_chart()
.settings(settings)
.y(function (d) { return +d; });
// Display
d3.select("#" + svgId)
.datum(barChartData)
.call(barChart);
JavaScript:
// This is an extend method that I found
function extend(base, sub) {
// Avoid instantiating the base class just to setup inheritance // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create // for a polyfill // Also, do a recursive merge of two prototypes, so we don't overwrite // the existing prototype, but still maintain the inheritance chain // Thanks to @ccnokesvar origProto = sub.prototype;
sub.prototype = Object.create(base.prototype);
for (var key in origProto) {
sub.prototype[key] = origProto[key];
}
// Remember the constructor property was set wrong, let's fix itsub.prototype.constructor = sub;
// In ECMAScript5+ (all modern browsers), you can make the constructor property // non-enumerable if you define it like this insteadObject.defineProperty(sub.prototype, 'constructor', {
enumerable: false,
value: sub
});
}
function generic_chart(){
var margin,
settings,
width,
height;
chart.margin = function (_) {
if (!arguments.length) return margin;
margin = _;
return this.chart;
};
settings = function (_) {
if (!arguments.length) return settings;
settings = _;
return this.chart;
};
// Other common chart.<variable> settings
}
generic_chart.prototype = {
settings: function () {
// Setup to call the settings function above
}
};
function bar_chart() {
var barChart-specific variables...;
function chart(selection) {
// Initialization of variables from bar_chart and generic_chart functions
selection.each(function (data, i) {
// Code that assembles data based on the variables
);
//Code that builds the axes
}
chart.x = function (_) {
if (!arguments.length) return xValue;
xValue = _;
return chart;
};
chart.y = function (_) {
if (!arguments.length) return yValue;
yValue = _;
return chart;
};
// Other chart.<variable> settings that are specific to a barChart implementation
return chart;
}
// Setup the prototype chain
extend(generic_chart, bar_chart);
Then, when I call this:
// Setup
var barChart = bar_chart()
.settings(settings)
.y(function (d) { return +d; });
It would use the settings function in the generic_chart and the y function in the bar_chart. Unfortunately, this just isn't working and every time I get close, something else breaks. Help!