Dismiss Notice
Join Physics Forums Today!
The friendliest, high quality science and math community on the planet! Everyone who loves science is here!

Javascript Inheritance questions

  1. Oct 10, 2015 #1

    Borg

    User Avatar
    Science Advisor
    Gold Member

    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:

    Code (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:
    Code (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:
    Code (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!
     
  2. jcsd
  3. Oct 10, 2015 #2

    jedishrfu

    Staff: Mentor

    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
    Code (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 { ... }

    ...
     
     
  4. Oct 10, 2015 #3

    Borg

    User Avatar
    Science Advisor
    Gold Member

    Thanks. I think that I looked at that page within the last few days but I'll give it some closer scrutiny.
    Yes, that's generally what I'm trying to do. If it was Java, I would have finished it in my sleep. :smile:
     
  5. Oct 10, 2015 #4

    Borg

    User Avatar
    Science Advisor
    Gold Member

    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.
     
  6. Oct 10, 2015 #5

    jedishrfu

    Staff: Mentor

    @Borg Im proud of you and your work ethic. Only a
    true programmer will spend days trying to get some small nit fixed.
     
  7. Oct 11, 2015 #6

    Borg

    User Avatar
    Science Advisor
    Gold Member

    Thank you for that. I try to do my best but sometimes it's best to try again later.
     
  8. Oct 11, 2015 #7
    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.

    Code (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: Oct 11, 2015
  9. Oct 11, 2015 #8

    Borg

    User Avatar
    Science Advisor
    Gold Member

    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.
     
  10. Oct 11, 2015 #9

    Borg

    User Avatar
    Science Advisor
    Gold Member

    Seems to be working with some minor changes to the way that I was previously calling variables and methods. :smile:
     
  11. Oct 11, 2015 #10
    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.
     
Know someone interested in this topic? Share this thread via Reddit, Google+, Twitter, or Facebook




Similar Discussions: Javascript Inheritance questions
  1. Javascript problem (Replies: 6)

Loading...