Javascript Inheritance questions

  • Java
  • Thread starter Borg
  • Start date
  • #1
Borg
Science Advisor
Gold Member
1,871
2,222

Main Question or Discussion Point

This is going to be a long post. :wideeyed:

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);
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:
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!
 

Answers and Replies

  • #2
11,349
4,819
There's a discussion here using a class extends strategy:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain

Look further down in the page for the class stuff which seems new to javascript although its been around a while in Java.

In Java I typically make a CommonMethods class and do something like this
Java:
class CommonPlot {

  public method1(...) { ... }     // callable via the instance or in the subclass

  protected method2( ... ) { ... }    // callable inside the subclass but not via the instance

}

class LinePlot extends CommonPlot {  ... }

class BarPlot extends CommonPlot { ... }

...
 
  • #3
Borg
Science Advisor
Gold Member
1,871
2,222
There's a discussion here using a class extends strategy:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain

Look further down in the page for the class stuff which seems new to javascript although its been around a while in Java.
Thanks. I think that I looked at that page within the last few days but I'll give it some closer scrutiny.
In Java I typically make a CommonMethods class and do something like this
Yes, that's generally what I'm trying to do. If it was Java, I would have finished it in my sleep. :smile:
 
  • #4
Borg
Science Advisor
Gold Member
1,871
2,222
Well, still no luck. I've got lots to build and I've spent three full days trying to clean this one small thing. At this point, it isn't worth it anymore unless someone has any better ideas.
 
  • #5
11,349
4,819
Well, still no luck. I've got lots to build and I've spent three full days trying to clean this one small thing. At this point, it isn't worth it anymore unless someone has any better ideas.
@Borg Im proud of you and your work ethic. Only a
true programmer will spend days trying to get some small nit fixed.
 
  • #6
Borg
Science Advisor
Gold Member
1,871
2,222
@Borg Im proud of you and your work ethic. Only a
true programmer will spend days trying to get some small nit fixed.
Thank you for that. I try to do my best but sometimes it's best to try again later.
 
  • #7
521
70
There are many different ways to do inheritance in JavaScript. Looking at your code it seems you are using closures to create your objects. Here is an example of how inheritance can be done while sticking with your programming style. This also has the advantage that it behaves similar to Java inheritance and that means Java code would be very easy to translate into JavaScript. btw. just as in Java, this inheritance pattern only allows single inheritance.

JavaScript:
//contructor for generic chart
function Chart(arg1, arg2, ...) {
  //private variables
  var width, height, ...;
  //we use self instead of this
  //no need to bother with the broken behavior of "this" in JS
  var self = {};
  //private methods
  function privateMethod1() {
  }
  function privateMethod2() {
  }
  //public methods
  function method1() {
    ...
    //calling another public method
    //if subclasses were to override method2 it would not
    //affect this at all
    method2();
  }
  function method2() {
    ...
    //calling another public method through self
    //which means if subclasses override it we use the
    //version from the subclass
    self.method1();
  }
  //the instanceof operator doesn't work with this pattern
  //so we define our own
  function instanceOf(s) {
    return s === "Chart";
  }
  //adding public methods to self
  self.method1 = method1;
  self.method2 = method1;
  self.instanceOf = instanceOf;
  return self;
}

function BarChart(arg1, arg2, ...) { //extends Chart
  var privateVar1, privateVar2, ...;
  //extending Superclass
  var self = Chart(arg1, arg2, ...);
  //@Override
  function method1() {
  }
  function method3() {
  }
  var super_instanceOf = self.instanceOf;
  function instanceOf(s) {
    return s === "BarChart" || super_instanceOf(s);
  }
  self.method1 = method1;
  self.method3 = method3;
  self.instanceOf = instanceOf;
  return self;
}
 
Last edited:
  • #8
Borg
Science Advisor
Gold Member
1,871
2,222
Thanks. I will give that a try. As you say, there are a lot of different ways to do inheritance. I think that part of the problem that I'm having is trying to mix ideas from various tutorials that don't play well with each other. Your example is a pattern that I haven't seen so I will be interested to see how it works for me.
 
  • #9
Borg
Science Advisor
Gold Member
1,871
2,222
Seems to be working with some minor changes to the way that I was previously calling variables and methods. :smile:
 
  • #10
521
70
Great, btw. I modified my example and added instanceOf methods since the instanceof operator doesn't work with this pattern. Of course you could also define a global instanceOf function instead.
 

Related Threads for: Javascript Inheritance questions

  • Last Post
Replies
1
Views
1K
  • Last Post
Replies
1
Views
1K
Replies
14
Views
3K
  • Last Post
Replies
5
Views
636
Replies
3
Views
1K
  • Last Post
Replies
3
Views
3K
  • Last Post
Replies
4
Views
2K
  • Last Post
Replies
11
Views
2K
Top