是否可以在这里避免全局?值得吗?

Is it possible to avoid a global here? And is it worth it?

我有一个显示 google 地图的网络应用程序。当用户单击地图上多边形的某处时,同一页面上的 kendo 网格将通过 kendo 数据源代表的 ajax 调用进行填充。

当 kendo 网格获取数据时,我也使用相同的数据填充 google 地图上的工具提示。此工具提示需要显示在用户单击的同一位置。

为了传递这次点击的坐标,我使用了一个全局变量。 我想知道是否可以避免它,以及是否值得麻烦。

以下是我的代码的摘录。

定义全局:

var clickLatLng;

订阅地图点击事件:

google.maps.event.addListener(polygon, 'click', function(event) {
    //Keep track of coordinates in a global
    clickLatLng = event.latLng;

    // initiate the ajax call to get the data
    dataSource.read(this.objInfo.id);

    // update grid view
    var grid = $("#grid").data("kendoGrid");
    grid.setDataSource(dataSource);
    grid.refresh();
});

定义数据源:

var dataSource = new kendo.data.DataSource({
    transport: {
        read: {
            url: "/getdata",
            type: "GET",
            dataType: "json",
            contentType: "application/json"
        }
    },
    schema: {
        data: function (response) {
            var events = [];

            var eventList = response.EventList;
            if (!eventList) {
                return events;
            }

            // This fills events from eventList
            populateEvents(events, eventList);

            // see below
            setToolTipValues(events);

            return events;
        },
        ...
    }
});

显示工具提示:

var setToolTipValues = function (events) {
    var infoWindow = new google.maps.InfoWindow();
    infoWindow.setContent(getTooltipConent(events));
    // here we use the global saved earlier to display the tooltip at the correct coordinates
    infoWindow.setPosition({ lat: clickLatLng.lat(), lng: clickLatLng.lng() });
    // map is also global I'm guessing there is no work around that
    infoWindow.open(map);
}

更新: 补充一些信息,上面的代码位于pageHelper.js pageHelper.js 看起来像这样:

var pageHelper = function($, google, model) {

    var clickLatLng;
    ...    
}

这是它在页面中的调用方式:

<script type="text/javascript">

    var model = ...;  //the ... bit comes from the server
    pageHelper($, google, model);
</script>

更新 2 事件顺序:

如果你想存储一个值供其他事件稍后使用,你有几个非全局的选择:

  1. 您可以创建一个通用闭包(通常是 IIFE),其中包含需要访问该值并将该值存储在闭包中的局部变量中的所有代码。

  2. 您可以将该值存储为某个现有对象的 属性,所有需要使用该值的代码都可以访问该对象。这可能是一个 DOM 对象或您代码中的其他对象。 jQuery 的 .data() 提供了一种无需使用全局变量即可将值与 DOM 对象相关联的简单方法。

  3. 您可以创建单个全局命名空间对象并将您的值作为 属性 放在该单个命名空间对象上。虽然这使用了一个全局变量,但它允许您拥有多个全局属性,同时仅使用全局命名空间中的一个名称。

  4. 如果该值被用作一个主事件触发器的所有部分,那么您通常可以找到一种方法来传递变量,以便事件处理的其他部分可以使用原始值而不是而不是将其存储在全局中。我无法在您的代码中说出事件的确切顺序以了解这是否可能。

一般来说,出于各种原因避免使用全局变量是个好主意。这不是一个硬性规定,但它是一个被普遍认为是好的做法的指南。如果您发现需要多个全局变量,您也可以使用单个全局命名空间对象(这就是像 jQuery 这样的框架所做的)所以您只创建一个新的全局可访问符号。


根据 OP 的一些说明更新:

听起来您根本就没有将它存储在全局变量中,而是存储在闭包中的局部变量中。这可能就是您需要做的所有事情,因为将它存储在其他地方确实没有太多优势。

因为你想在一个被系统其他部分间接调用的方法中使用值,你不能直接将值作为函数参数传递,所以典型的 Javascript 解决方案这是将它存储在父闭包中,然后您可以在稍后调用时从回调函数访问它。这是此类问题的常用 Javascript 设计模式。

所以,如果这个理解是正确的:

  1. 用户点击多边形。
  2. 您从数据源读取数据。
  3. 数据源中的 schema.data 方法由 kendo 框架调用,作为从该数据源读取的一部分。
  4. 您需要在那个 schema.data 回调中使用点击位置。

然后,您在这里可以做的最好的事情就是将值放在父闭包中,这样 scheme.data 回调可以到达原始点击位置。


我可能会亲自对您的代码结构进行一次更改。我可能会将位置作为参数传递给 setToolTipValues(),并获取闭包变量以在 schema.data() 内传递给它。这使得 setToolTipValues() 独立于位置的获取方式,并将对该闭包变量的依赖保持在一个无法避免的地方。