How can I make this javascript easier to read, maintain and understand from the background of OO?

I come from the land of Java, C #, etc. I am working on a javascript processing engine for a web application that I have. I am using jQuery, AJAX, etc. I'm having difficulty getting everything to work the way I feel it needs to be - for example, I went over to what seems too complicated to make sure that when I make an AJAX call, my callback accesses the members of the object. These callback functions shouldn't be so complicated, right? I know that I must do something wrong. Please indicate what I could do better - let me know if the provided fragment is too much / too little / too scary to watch.

What am I trying to do:

  • When loading the page, I have a choice full of users.
  • I create reports (1 at the moment) and add them to the selection box.
  • When both user and report are selected, I run the report.
  • The report includes creating a series of calls - receiving a series of exercises, leagues and tournaments - for each league and tournament, it receives all these series, and then for each series it captures all games.
  • It maintains a counter of active calls, and when all of them are completed, the report is launched and displayed to the user.

code:

//Initializes the handlers and reports
function loadUI() {
    loadReports();
    $("#userSelect").change(updateRunButton);
    $("#runReport").click(runReport);
    updateRunButton();
    return;
    $("#userSelect").change(loadUserGames);
    var user = $("#userSelect").val();
    if(user) {
        getUserGames(user);
    }
}

//Creates reports and adds them to the select
function loadReports() {
    var reportSelect = $("#reportSelect");
    var report = new SpareReport();
    engine.reports[report.name] = report;
    reportSelect.append($("<option/>").text(report.name));

    reportSelect.change(updateRunButton);
}

//The class that represents the 1 report we can run right now.
function SpareReport() {
    this.name = "Spare Percentages";
    this.activate = function() {

    };

    this.canRun = function() {
        return true;
    };

    //Collects the data for the report.  Initializes/resets the class variables,
    //and initiates calls to retrieve all user practices, leagues, and tournaments.
    this.run = function() {
        var rC = $("#rC");
        var user = engine.currentUser();
        rC.html("<img src='/img/loading.gif' alt='Loading...'/> <span id='reportProgress'>Loading games...</span>");
        this.pendingOperations = 3;
        this.games = [];
        $("#runReport").enabled = false;
        $.ajaxSetup({"error":(function(report) {
            return function(event, XMLHttpRequest, ajaxOptions, thrownError) {
                report.ajaxError(event, XMLHttpRequest, ajaxOptions, thrownError);
            };
        })(this)});

        $.getJSON("/api/leagues", {"user":user}, (function(report) {
            return function(leagues) {
                report.addSeriesGroup(leagues);
            };
        })(this));
        $.getJSON("/api/tournaments", {"user":user}, (function(report) {
            return function(tournaments) {
                report.addSeriesGroup(tournaments);
            };
        })(this));
        $.getJSON("/api/practices", {"user":user}, (function(report) {
            return function(practices) {
                report.addSerieses(practices);
            };
        })(this));
    };

    // Retrieves the serieses (group of IDs) for a series group, such as a league or
    // tournament.
    this.addSeriesGroup = function(seriesGroups) {
        var report = this;
        if(seriesGroups) {
            $.each(seriesGroups, function(index, seriesGroup) {
                report.pendingOperations += 1;
                $.getJSON("/api/seriesgroup", {"group":seriesGroup.key}, (function(report) {
                    return function(serieses) {
                        report.addSerieses(serieses);
                    };
                })(report));
            });
        }
        this.pendingOperations -= 1;
        this.tryFinishReport();
    };

    // Retrieves the actual serieses for a series group.  Takes a set of
    // series IDs and retrieves each series.
    this.addSerieses = function(serieses) {
        var report = this;
        if(serieses) {
            $.each(serieses, function(index, series) {
                report.pendingOperations += 1;
                $.getJSON("/api/series", {"series":series.key}, (function(report) {
                    return function(series) {
                        report.addSeries(series);
                    };
                })(report));
            });
        }
        this.pendingOperations -= 1;
        this.tryFinishReport();
    };

    // Adds the games for the series to the list of games
    this.addSeries = function(series) {
        var report = this;
        if(series && series.games) {
            $.each(series.games, function(index, game) {
                report.games.push(game);
            });
        }
        this.pendingOperations -= 1;
        this.tryFinishReport();
    };

    // Checks to see if all pending requests have completed - if so, runs the
    // report.
    this.tryFinishReport = function() {
        if(this.pendingOperations > 0) {
            return;
        }
        var progress = $("#reportProgress");
        progress.text("Performing calculations...");
        setTimeout((function(report) {
            return function() {
                report.finishReport();
            };
        })(this), 1);
    }

    // Performs report calculations and displays them to the user.
    this.finishReport = function() {
        var rC = $("#rC");

        //snip a page of calculations/table generation
        rC.html(html);

        $("#rC table").addClass("tablesorter").attr("cellspacing", "1").tablesorter({"sortList":[[3,1]]});
    };

    // Handles errors (by ignoring them)
    this.ajaxError = function(event, XMLHttpRequest, ajaxOptions, thrownError) {
        this.pendingOperations -= 1;
    };

    return true;
}

// A class to track the state of the various controls.  The "series set" stuff
// is for future functionality.
function ReportingEngine() {
    this.seriesSet = [];
    this.reports = {};
    this.getSeriesSet = function() {
        return this.seriesSet;
    };
    this.clearSeriesSet = function() {
        this.seriesSet = [];
    };
    this.addGame = function(series) {
        this.seriesSet.push(series);
    };
    this.currentUser = function() {
        return $("#userSelect").val();
    };
    this.currentReport = function() {
        reportName = $("#reportSelect").val();
        if(reportName) {
            return this.reports[reportName];
        }
        return null;
    };
}

// Sets the enablement of the run button based on the selections to the inputs
function updateRunButton() {
    var report = engine.currentReport();
    var user = engine.currentUser();
    setRunButtonEnablement(report != null && user != null);
}

function setRunButtonEnablement(enabled) {
    if(enabled) {
        $("#runReport").removeAttr("disabled");
    } else {
        $("#runReport").attr("disabled", "disabled");
    }

}

var engine = new ReportingEngine();

$(document).ready( function() {
    loadUI();
});

function runReport() {
    var report = engine.currentReport();
    if(report == null) {
        updateRunButton();
        return;
    }
    report.run();
}

I'm going to start adding new reports, some of which will only work with a subset of custom games. I'm going to use subclasses (prototype?), But if I can't figure out how to simplify some of these ... I don't know how to end this sentence. Help!

+3
5
$.getJSON("/api/leagues", {"user":user}, (function(report) {
        return function(leagues) {
            report.addSeriesGroup(leagues);
        };
    })(this));

:

var self = this;
$.getJSON("/api/leagues", {"user":user}, (function(leagues) {
            self.addSeriesGroup(leagues);
        });

- , , .

+3

"" .

+2

, . , , - JavaScript, , .

var x = 1;
$.ajax({
  success: function () {
    alert(x);
  }
});

, AJAX, x "1" .

+1

. . (, , , , , , , )

, , , .

+1

:

function Parent(x) {
  this.x = x;  /* Set an instance variable. Methods come later. */
}

/* Make Parent inherit from Object by assigning an
 * instance of Object to Parent.prototype. This is
 * very different from how you do inheritance in
 * Java or C# !
 */
Parent.prototype = {  /* Define a method in the parent class. */
  foo: function () {
    return 'parent ' + this.x;  /* Use an instance variable. */
  }
}

function Child(x) {
  Parent.call(this, x)  /* Call the parent implementation. */
}

/* Similar to how Parent inherits from Object; you
 * assign an instance of the parent class (Parent) to
 * the prototype attribute of the child constructor
 * (Child).
 */
Child.prototype = new Parent();

/* Specialize the parent implementation. */
Child.prototype.foo = function() {
  return Parent.prototype.foo.call(this) + ' child ' + this.x;
}

/* Define a method in Child that does not override
 * something in Parent.
 */
Child.prototype.bar = function() {
  return 'bar';
}

var p = new Parent(1);
alert(p.foo());

var ch = new Child(2);
alert(ch.foo());
alert(ch.bar());

jQuery, , ( -) , .

, , , OO JS, .

+1

Source: https://habr.com/ru/post/1706558/


All Articles