使用单独的JavaScript对象在许多其他JS对象之间共享数据是否更好?

时间:2015-07-25 03:32:12

标签: javascript

我正在使用PHP和JavaScript构建项目管理应用程序。这个问题更多地围绕着它的JavaScript方面,更多的是一个架构问题。

所以我有多个JavaScript对象/类类型文件,基本上就像" modules" ...

  • TaskModal.js
  • ProjectForums.js
  • TaskList.js

仅列举一些处理应用程序部分的JavaScript文件。

还有更多。但他们都有一个共同点,那就是他们都需要共享访问某些数据。

例如用户列表。我的大多数JavaScript文件都需要访问数据库中所有用户的用户列表。

为了避免我的所有JS文件查询数据库以构建单独的Userlist,我改为计划将一个Global JS var注入一个带有PHP的JSON对象,它将保存Userlist并允许所有JS文件访问1个用户列表! / p>

为了更进一步,我在我的第一个JS文件中构建了一个JS函数,它将检查是否存在从PHP创建的全局用户列表,如果找不到它,那么它将发出一个AJAX请求,得到名单本身!

如果必须使用AJAX获取列表,它会将其设置为其他JS文件的全局userList var,以避免同时出现自己的AJAX请求。

因此,用户列表将被缓存以供所有人使用冗余回退进行访问,以确保它被加载!

在我的TaskModal.js文件中,我已添加此AJAX后备文件以检查全局范围内的用户列表,然后如果在Global中找到它,则会将其缓存到本地var。如果没有那么它就会发出AJAX请求。

有了这个理论和设计,我需要基本上在我的所有JS文件中复制相同的用户函数,我想也许这不是他最好的方式......

我是否应该有一个单独的User对象来处理所有上述功能,而单独的JS文件上用户的唯一功能就是访问这个User对象?

下面是一个简单的示例,说明TaskModal.js对象可能具有自己的用户功能。是否应该将所有这些移动到单独的User对象?如果是的话,是否可以轻松访问我所有JS文件中的用户数据?

(function (window, document, $, undefined) {
    "use strict";

    //we cache a few useful values, like jQuery wrapped window and document
    var $window = $(window),
      $document = $(document),

    projectTaskModal = {

      // Cache Properties
      cache: {

        isUserObjLoaded: false,
        userList: '',
        isAjaxRequestPending: false,
        getUserListUrlEndpoint: '/post',
        user: {},
        deferred: '',
      },


      /**
       * Initialize projectTaskModal Object and fire off some other init functions
       * @return {[type]} [description]
       */
      init: function() {
        projectTaskModal.users.initUserList();
        projectTaskModal.users.getUserList();
        projectTaskModal.cache.user = projectTaskModal.users.getUserById('1gdfgdfkn123423423');

        console.log('cached User object:', projectTaskModal.cache.user);
      },



      // Get Userlist and other user related functions
      users: {

        initUserList: function() {

          // check for local Flag indicating that Userlist has been loaded.
          // a userlist loaded into DOM as Global var from PHP
          if(projectTaskModal.cache.isUserObjLoaded){

            // Local copy of userlist has already been loaded so we will not search for
            // it in the Global DOM + Not make a server AJAX request to load it
            //
            // Just return our local cached copy of Userlist
            return projectTaskModal.cache.userList;

          }else{

            // this.cache.isUserObjLoaded is FALSE so we must search Global DOM for userlist
            // generated from PHP.

            if (!window.hasOwnProperty('globalUserList')) {

alert('Global userlist is not loaded so load it ourself using AJAX request');
              // Global userlist is not loaded so load it ourself using
              // AJAX request and Promise.
              var loadUserListAjax = projectTaskModal.users.ajaxLoadUserListData()
              .done(function(response) {
                    console.log(response);
                    alert('AJAX request success');

                      // Parse JSON response var
                      var userListJson = response;

                      // next set FLAG that userList is loaded
                      projectTaskModal.cache.isUserObjLoaded = true;

                       // After loading userlist from server, cache it locally and to global var for other
                      // JS scripts to use and prevent them from also making AJAX requests!

                      projectTaskModal.cache.userList = userListJson;

                      window.globalUserList = userListJson.user_list;

                      projectTaskModal.cache.isAjaxRequestPending = false;

                      projectTaskModal.users.getUserList();


              }).fail(function(response) {
                  // AJAX request failed
                  console.log('response', response);
                  alert('AJAX request failed');
              }).always(function(response) {
                  projectTaskModal.cache.isAjaxRequestPending = false;

                  // Hide Loader/Spinner
                  setTimeout($.unblockUI, 1000);
              });



            }else{

              alert('Global userlist is loaded so cache it locally and do not make AJAX request!');
              // cache the Global userlist var to local property
              projectTaskModal.cache.userList = window.globalUserList;

              // next set FLAG that userList is loaded
              projectTaskModal.cache.isUserObjLoaded = true;

              projectTaskModal.users.getUserList();

            }
          }
        },


        ajaxLoadUserListData: function() {
            projectTaskModal.cache.isAjaxRequestPending = true;
            return $.ajax({
              type: 'POST',
              async: true,
              contentType: 'application/json; charset=utf-8',
              dataType: 'json',
              url: '/restful/fortune',
              data: {
                action: 'load-userlist'
              },
            });
        },



        getUserList: function() {
            if(projectTaskModal.cache.isUserObjLoaded){

                console.log('userList',projectTaskModal.cache.userList);
            }else{
                console.log('userlist Obj not loaded =(');
            }
        },


        getCurrentUser: function(user_id) {
            if(projectTaskModal.cache.isUserObjLoaded){
                myUserData[1].username
                console.log('userList',projectTaskModal.cache.userList);
            }else{
                console.log('userlist Obj not loaded =(');
            }
        },


        getUserById: function(user_id) {
          var userList = projectTaskModal.cache.userList;
          for (var i = 0; i < userList.length; i++) {
            if (userList[i].id === user_id) {
               user = {
                  id: userList[i].id,
                  username: userList[i].username,
                  role: userList[i].role,
                  gravatar: userList[i].gravatar,
               }
              return user;
            }
          }
          throw "Couldn't find object with user_id: " + user_id;
        },

      },
    };


    // Run Init on DOM Ready
    $(function() {
        projectTaskModal.init();

    });

}(this, document, jQuery));

浪费记忆?

使用当前方法,我没有单独的User对象。我基本上将用户列表存储在:

  • 全局变量
  • 3个独立JS对象文件中的每一个的本地var

这似乎可能会浪费大量内存?将用户列表和数据存储在自己的专用User对象中会将4x Userlist减少到1?

快速阅读 - 目标大纲

我的Project Management App中有3个或更多JavaScript文件/对象,它们都需要访问共享数据,例如UserlistDebug/MockingMilestones ListTags List,还有更多!

我希望加载一次并在所有JS对象/文件之间缓存和共享,而不是让每个这些目标文件从服务器加载数据,而AJAX请求分别在同一页面加载时反复加载相同的内容。 / p>

由于我是JS的新手,我不确定在不使用第三方库的情况下完成此任务的最佳途径! (jQuery没问题)

为简单起见,假设有3个JS对象/文件,用户列表是唯一可以在它们之间共享的东西。

在页面加载时我首先尝试使用PHP将Userlist作为JSON对象加载到DOM作为全局变量。

如果找不到它,我希望有一个回退,以便JS可以根据需要发出加载用户列表的AJAX请求。 IT会加载用户列表,在本地为执行工作的对象缓存它,然后将其作为全局变量缓存,就像PHP应该做的那样!在全局缓存之后,检查全局用户列表的下一个JS文件应该找到它并将其本地缓存到自身,而不需要做任何服务器请求。

我担心的是,我需要在所有3个JS对象/文件中复制这种加载/缓存机制!

另外我认为它可能导致大量内存可以避免?

所以我的主要问题是我上面描述的方式,看起来像一个不好的方法?我提到的问题是真正的问题吗?然后,如果这一切都是真的,那么另类更好的路线是什么?

我在想,或许不必将所有这些用户代码复制到3个对象/ JS文件中,也许它可能是它自己的User{}对象。

如果有一个User对象来处理加载Userlist并检查它; s存在以及所有这些东西,那么我如何最好地访问其他3个JS对象/文件中的这个User对象数据?

我希望这听起来很有意义,我很难解释它。

1 个答案:

答案 0 :(得分:1)

您可以通过使一组js文件处理数据层来进一步分离您的关注点。我们可以将这些services命名为。另一组modules将处理应用程序流(或UI插件,例如图库)并使用数据层管理的数据。这听起来很漂亮和东西,让我们跳转到我喜欢在某些项目中使用的模式的代码。

在我加载的第一个js文件中,我为项目定义了一个全局命名空间

// main.js
(function(){
    window.MyCoolProject = {
        services: {},
        modules: {},
        helpers: {},
        laserGuns: {},
        etc: {}
    };
})();

这样我们以有组织的方式将我们的公共方法暴露给我们的其余代码,并避免与其他js库的冲突。然后我们加载数据层。

// services/user.js
// this file has all the functions required to load user data, these will be a bunch of ajax calls

(function(){

    // we can use this to cache data, even though we can cache via jquery. more on this later
    var cached = {};

    MyCoolProject.services.UserService = {
        getUsers: function(callback){
            // if the cache object has a 'users' property, we have already loaded this data so we returned the cached version of this. Note that we don't return data, we pass it through a callback as a asynch call
            if (cache.users){
                 callback(cache.users);
                 return;
            }
            // if there is no cached data, we make the ajax call
            $.ajax({
                 url: 'path/to/api.json'
                 method: 'GET',
                 // jquery also provides a cache for ajax calls, you can experiment with both to see which better suits your needs
                 cache: true,
                 success: function(data){
                     // once loaded we can manually cache the data
                     cache.users = data;
                     callback(cache.users);
                 }
            });
        },
        getUser: function(id, callback){ ... },
        etc: function(callback){ ... }
    };
})();

这种方式,当你打电话

MyCoolProject.services.UserService.getUsers(function(users){
    console.log(users); // will be the same list of users, despite how many times you call it. You will get the same cached version
});

进入模块。我相信这些代码可能与您已有的代码相同,但改变了数据加载方式,如上所述在服务层中调用函数。您可以在不同的文件中调用相同的函数,并且不会有重复的数据。但是有一个js gotcha!

在js中,对象(和数组)作为彼此的引用存储。这意味着做的事情如下:

var a = {},
    b = a,
    c = b;

表示ac相同,a的任何更改都会反映在c中。在内部,它是相同的记忆块,但我确信有更多人比我更有资格解释这一点。如果将相同的数据对象拖到其他不同的位置,基本上不需要担心内存消耗。您必须注意的是,一个模块不会更改该数据,因为它将针对所有其他模块进行更改。如果在某些情况下你需要改变数据对象,你必须先克隆它(参见jquery中的$ .extend())。

根据所有这些mumbo jumbo,一个模块看起来像这样

// modules/userImageGallery.js
// a strange image gallery that shows a mugshot of each user
(function(){
    // we autoexecute the module and push its public methods into its namespace
    MyCoolProject.modules.UserImageGallery = (function(){

        var users = [];

        MyCoolProject.services.UserService.getUsers(function(data){
            users = data;
        });

        function next(){ currentUserIndex++; }
        function prev(){ currentUserIndex--; }

        ... etc ...

        // we expose the desired methods
        return{
            next: next,
            prev: prev
        };
    })();
})();

// now we, or any other module, can control the image gallery by calling
MyCoolProject.modules.UserImageGallery.next();

请注意,javascript中有几种模式可供使用。有闭包,原型,单例(带函数的对象)等。重要的是公共函数被推入相应的全局命名空间。

我希望你现在看到这对你的代码组织有什么好处,你可以继续增长,增加模块和服务,保持一切有条理,但也可以访问其他人。另一个好处是您可以通过控制台访问这些方法,这样您就可以更轻松地运行测试。

展望未来

鉴于你提到你是javascript的新手,我建议你采用像你一样的无框架方法。通过这种方式,您将学习js的所有调整和特权。一旦您感到舒服,请考虑调查角度或余烬或任何其他数据绑定框架。这些可以改善关注点分离,更好地控制应用程序流,提高安全性和依赖关系管理。希望你喜欢骑行!