javascript - 我怎样才能使这个 javascript 更易于阅读、维护和从 OO 背景理解?

标签 javascript

我来自 Java、C# 等领域。我正在为我拥有的 Web 应用程序开发一个 JavaScript 报告引擎。我正在使用 jQuery、AJAX 等。我很难让事情按照我认为应该的方式工作——例如,我已经做了一些看起来很麻烦的事情来确保当我进行 AJAX 调用时,我的回调有访问对象的成员。那些回调函数不需要那么复杂,不是吗?我知道我一定做错了什么。请指出我可以做得更好的地方 - 让我知道所提供的片段是否太多/太少/太糟糕而无法查看。

我正在尝试做的事情:

  • 在页面加载时,我选择了全部用户。
  • 我创建报告(目前 1 个)并将它们添加到选择框中。
  • 同时选择用户和报告后,我会运行报告。
  • 该报告涉及进行一系列调用 - 获取练习系列、联赛和锦标赛 - 对于每个联赛和锦标赛,它会获取所有这些系列,然后针对每个系列获取所有比赛。
  • 它维护一个事件调用的计数器,当它们全部完成时,将运行报告并显示给用户。

代码:

//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();
}

我即将开始添加新报告,其中一些将仅针对用户游戏的一个子集运行。我将尝试使用子类(原型(prototype)?),但如果我不知道如何简化其中的一些……我不知道如何完成那句话。帮助!

最佳答案

$.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);
        });

当您在循环中并希望绑定(bind)到每次循环都会更改的变量时,函数返回函数会更有用。

关于javascript - 我怎样才能使这个 javascript 更易于阅读、维护和从 OO 背景理解?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/757976/

相关文章:

javascript - 浏览器在 javascript eval() 期间卡住

javascript - Angular 2 - Twitter 搜索 api - 预检时仅应用程序身份验证错误 400

javascript - 链接到另一个页面上的 javascript 链接

javascript - 如果任何表单输入发生更改,则更改(立即)提交按钮文本 [JS]

javascript - 在父子结构中重新排序 JS 对象

javascript - JS : Nested for loop while parsing JSON

javascript - 如何使用 ajax 调用填充选择框,该调用使用 dojo 返回字符串列表?

javascript - 如果对象内容是用户生成的,如何正确转义对象的 JSON 字符串?

javascript - React 组件 - 创建它们的正确方法是什么?

javascript - React中构造函数和属性的正确使用