在Javascript中设置功能的最佳方式(可能是通过关闭?)

时间:2012-12-31 23:34:27

标签: javascript json performance closures memory-efficient

我有一个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对象,就像我今天所做的那样,并以某种方式通过并更改对象类型以指向我创建的闭包?

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

...谢谢

-Ben

3 个答案:

答案 0 :(得分:1)

我认为你要说的是你想用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"

答案 1 :(得分:1)

正如其他人在评论中写的那样,目前尚不清楚你想要什么,但这听起来与你所描述的最接近:

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绑定的新闭包。

答案 2 :(得分: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 here applynew转换为对象{{1} }}关键字并将项数组传递给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]);
    

    这样你就不会在任何地方附加功能,你不会将对象转换为新的原型有线对象,但你必须以不同的方式调用它们。