【问题标题】:Best way to set up function in Javascript (likely via closure?)在 Javascript 中设置函数的最佳方法(可能通过闭包?)
【发布时间】:2012-12-15 22:05:39
【问题描述】:

我有一个 Web 应用程序,它调用一个 Web 服务,该服务返回一些表示对象数组的 JSON。每个对象都有一些字段。下面是一些示例 JSON 来给出一个想法:

{
   "data": [
      {
         "id": "12345",
         "from": {
            "name": "John Doe",
            "id": "6789"
         },
         "start_time": "2012-12-16T02:17:20+0000",
         "end_time": "2012-12-16T02:17:20+0000",
         "publish_time": "2012-12-16T02:17:20+0000"
         }
      },
      {
         "id": "8888",
         "from": {
            "name": "Jane Smith",
            "id": "011"
         },
         "start_time": "2012-12-16T02:17:20+0000",
         "end_time": "2012-12-17T02:17:20+0000",
         "publish_time": "2012-12-16T02:17:20+0000"
         }
      }
   ]
  }

一旦返回,我使用 jQuery 的 parseJSON() 方法将其膨胀为对象数组(保留“数据”值)。这一切都很好,但是在我拥有我的数组之后,我有几个函数可以在每个数组插槽上运行。例如,假设有一个名为 GetDuration() 的函数将打印出 end_time 和 start_time 之间的时间间隔。无论如何,我定义了几个函数(可能是 15 个),此时,我只需遍历整个数组并使用函数的副本对每个对象进行膨胀。示例

for (var i=0;i<data.length;i++)
 data[i].TimeLapse = function() { ... };

我认为这可以提高效率。现在,我认为每个数组项都有一个相同函数的单独副本,这不是必需的。我还注意到处理时间的滞后,我希望可以减少。我读过关于 javascript 闭包的文章,在这种情况下它们似乎可以帮助我,但我没有太多在闭包中编写函数的经验。我会设置闭包然后修改 JSON 以某种方式膨胀到闭包类型的对象吗?还是我会像今天一样膨胀到常规的 javascript 对象,然后以某种方式通过并更改对象类型以指向我创建的闭包?

任何想法或建议将不胜感激。

谢谢...

-本

【问题讨论】:

  • 您能否发布一个更完整的示例来说明您如何使用这些功能?他们如何传递数据,返回什么?如果我们看不到您目前的工作方式,很难给出建议。更少的 JSON,更多的代码。
  • 正如@AlexWayne 所说,我们真的不能给出任何建议,这真的取决于你想要做什么和完成什么。但是,如果您已经设置了功能,我会将其设置为通用功能,然后执行data[i].TimeLapse = GetDuration(data[i].start_time,data[i].end_time); 之类的操作
  • 闭包只有在它们“保持与定义它们的环境的绑定”时才有帮助。我没有立即看到这会有什么帮助。正如亚历克斯韦恩所说,一个完整的(最小的)例子会有所帮助。

标签: javascript json performance closures memory-efficient


【解决方案1】:
  1. 如果您必须遍历数据的每个元素,为什么不计算每个项目的持续时间并将其添加为属性?

  2. 如果您愿意将每个数据对象的所有属性复制到一个新的对象中,您可以在该对象的构造函数上使用原型,并将timeLapse 等函数添加到原型。然后,您的所有数据对象都被连接起来以通过原型继承工作。但这是很多额外的工作,可能效果不佳。

  3. 您可以使用 JSONP 或执行返回代码的变体,而不是直接使用 JSON。像这样的:

    {
       "data": [
          new DataItem(
             12345",
             {
                "name": "John Doe",
                "id": "6789"
             },
             "2012-12-16T02:17:20+0000",
             "2012-12-16T02:17:20+0000",
             "2012-12-16T02:17:20+0000"
          ),
          // ... and so on ...
       ]
    }
    

    您确实必须小心执行的代码,而不是直接的对象表示法(阻止执行代码,它只允许有值),但我相信它可以安全地完成。例如,您可以不说new DataItem(),而是为每个最终对象嵌入一个数组,然后遍历它们并使用the technique hereapplying 和new 关键字将数组转换为对象并传入项目数组到DataItem 以获取新的原型连接对象。

  4. data 数组中的每个项目添加一个函数理论上可能会占用比必要更多的内存。根据您想要权衡的时间与空间,还有其他选择。例如:

    function ItemWrapper(dataobj) {
        for (key in dataobj) {
           if (!dataobj.hasOwnProperty(key)) { continue; }
           this[key] = dataobj[ky];
        }
        this.timeLapse = (new Date(this.end_time)).getTime() - (new Date(this.start_time)).getTime(); // assign simple static properties
    }
    
    ItemWrapper.prototype.dynamicFn = function () {
       return this.x - this.y; // changeable properties accessed as functions
    }
    
    var currentObj = new ItemWrapper(data[23]);
    console.log(currentObj.timeLapse); // static
    console.log(currentObj.dynamicFn()); //dynamic
    

    问题是:您想在使用时间还是在解析时间执行此包装?在解析时这样做实际上是我的建议 #2。但这个建议是仅在您需要特定项目时才这样做。我不知道您的项目访问模式,因此这可能并不理想,但至少是一种选择。

  5. 使用applycall

    function timeLapse() {
       return (new Date(this.end_time)).getTime() - (new Date(this.start_time)).getTime();
    }
    
    var Item16Elapsed = timeLapse.apply(data[16]);
    

    这样您就不会到处附加函数,也不会将对象转换为新的原型连接对象,但您必须稍微不同地调用事物。

【讨论】:

    【解决方案2】:

    我想你的意思是你想使用 JSON 来设置不同类型对象的属性,每个对象都有自己的方法?目前尚不清楚您在追求什么,或者目前正在做什么。但如果我是对的,那你肯定做错了。

    简而言之,不要向您的数据添加函数。相反,使用可以在创建时接受数据的方法创建类。

    // Constructor, accepts an object expected to have data about a user
    var User = function(data) {
    
      // save a field from the passed in user data on this instance
      this.name = data.name;
    };
    
    // a method every User object will have that uses some bit of user data
    User.prototype.sayHi = function() {
      alert('Hi, my name is ' + this.name);
    };
    
    // A place to put our new user objects
    var users = [];
    
    // An array of objects to iterate through
    var jsonData = [
      {name: 'Joe'},
      {name: 'Sally'}
    ];
    
    // Iterate through the array
    for (var i = 0; i < jsonData.length; i++) {
    
      // make a user, passing the constructor the data object for that user
      users.push( new User(jsonData[i]) );
    }
    
    users[0].sayHi(); // alerts: "Hi, my name is Joe"
    users[1].sayHi(); // alerts: "Hi, my name is Sally"
    

    【讨论】:

      【解决方案3】:

      正如其他人在 cmets 中所写,不清楚你想要什么,但这听起来最接近你所描述的:

      function make_duration_fun(x) {
          return function() { return GetDuration(x.start_time, x.end_time); };
      }
      

      然后你的循环可以做:

      data[i].TimeLapse = make_duration_fun(data[i]);
      

      只有一个持续时间函数,但每次调用 make_duration_fun 时,都会得到一个具有相同函数和不同 x 绑定的新闭包。

      【讨论】:

      • 感谢大家的帮助,如果我的问题缺乏细节,我很抱歉。我已经开始做出改变了。根据 Erik 的评论,我注意到一些可以简单地用属性替换的函数。进行此更改,我希望我能获得内存和效率提升(无需在堆栈中跳转)。这需要权衡:如果数据发生变化,属性将反映一个过时的值。不过,对于某些属性,这不是问题,因此这是一个值得权衡的选择。在其他一些情况下,保留函数是有意义的,因为数据可能会发生变化......
      • 我想反映最新的数据。为了提供更多细节,函数(和属性)是通过 KnockoutJS 绑定的。所以有属性、函数,还有一些 KO.Computed 函数。有趣的是,KO.Computed 有一个称为“deferEvaluation”的功能(这是不言自明的)。应用此功能后,创建函数可能仍会产生一些额外的内存开销,但至少您将节省一些 CPU 周期。我的下一步是应用 Alex 和 Barmar 描述的优化来更有效地插入剩余的函数。感谢大家为我提供的信息!
      • 另外,我之前提到我怀疑 JavaScript 闭包可能有助于以更有效的方式设置函数。我的意思是说 javascript 对象原型,这是 Alex 给我指出的方向。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-10-03
      • 1970-01-01
      • 2014-02-24
      • 2023-03-13
      • 2018-11-28
      • 2014-01-12
      • 1970-01-01
      相关资源
      最近更新 更多