1

我想定义一个管理消息的 Javascript 对象。在这个对象中,我需要一个可以执行 push() 的数组:

MsgObjCollection.push(MsgObj)

本质上,我试图用一堆 MsgObjs 填充 MsgObjCollection 对象。每个 MsgObj 都有 3 个变量 messagesText、timeStamp、source(发送或接收)。

另外,我需要一些方法,例如:

MsgObjCollection.Sent.Count       // Counts the number of sent messages
MsgObjCollection.Received.Count   // Counts the number of received messages
MsgObjCollection.Count            // Counts the total number of messages in the object

我不确定如何以最简单、最干净的方式解决这个问题。

注意:如果有任何混淆,这些不是静态方法。我将使用 new 运算符创建这些对象的实例。所以我需要多个实例

4

3 回答 3

2

这是对bfavaretto 答案的一个调整,它应该让你更接近你想要的:

function MsgObjCollection() {
    this.sent = [];
    this.received = [];
    this.total = [];

    this.push = function(msg) {
        // Assuming msg.source is either 'sent' or 'received',
        // this will push to the appropriate array.
        this[msg.source].push(msg);

        // Always push to the 'total' array.
        this.total.push(msg);
    };
};

您可以按如下方式使用它:

var coll = new MsgObjCollection();
coll.push(/* whatever */);

var sent = coll.sent.length;
var received = coll.received.length;

如果你愿意,你可以用暴露函数而不是属性的对象包装sentand数组;但这让我觉得没有必要。receivedCountlength

于 2013-06-16T19:22:08.603 回答
1

您需要推送、计数,您可能希望拥有所有数组方法/访问器/迭代器。更重要的是,如果你让你的集合成为一个数组,你会得到一些速度提升。

所以最好的解决方案是从数组继承,让你的对象只是真正的数组:什么都不应该在对象上定义,一切都在它的原型上。

-->> 您将免费获得数组的速度和所有功能。

函数看起来像:

function MsgObjCollection() { /* nothing */ };
var SO_pr =  ( MsgObjCollection.prototype = [] ) ;

然后,要在原型上定义计数,发送和接收,使用 Object.defineProperty 不污染枚举,并且还有 getters/setters :

Object.defineProperty(SO_pr, 'sent', { get : function() { 
                          var cnt = 0; 
                          this.forEach( function(x) { if (x.source == 'Sent') cnt++; }); 
                          return cnt; } } );
Object.defineProperty(SO_pr, 'received', { get : function() { 
                           var cnt = 0; 
                           this.forEach( function(x) { if (x.source == 'Received') cnt++; });
                           return cnt; } } );
Object.defineProperty(SO_pr, 'count', { get  : function()   { return this.length } , 
                                        set  : function (x) { this.length = x    } });

请注意,由于 Msg 集合的原型是一个数组,因此在更改 MsgObjCollection 的原型时不会污染数组的原型。

您希望的 Sent 和 Received 属性更复杂:它们充当底层对象的视图。
您可以做的一件事是让它们返回一个由原始数组的正确项构建的新数组。
不过,我更喜欢围绕原始数组构建一个包装器 1) 以允许通过此视图进行修改和 2) 避免产生垃圾。

小提琴在这里:http: //jsfiddle.net/cjQFj/1/

Object.defineProperty(SO_pr, 'Sent',    
                      { get : function() { return getWrapper('Sent', this); } } ) ;
Object.defineProperty(SO_pr, 'Received', 
                      { get : function() { return getWrapper('Received', this); } } ) ;

function getWrapper(wrappedProp, wrappedThis) {
   var indx = 0, wp=null;
   // try to find a cached wrapper
   while (wp = getWrapper.wrappers[indx++] ) { 
           if (wp.wthis === this && wp.wprop==wrappedProp) return wp.wrapper;
   };
  // no wrapper : now build, store, then return a new one
  var newWrapper = { 
       get count() { return (wrappedProp=='Sent') ? wrappedThis.sent : wrappedThis.received },
       unshift : function () {  if (this.count == 0) return null;
                         var indx=0; 
                         while (wrappedThis[indx].source != wrappedProp ) indx++; 
                         var popped = wrappedThis[indx];
          while (indx<wrappedThis.length-1) {wrappedThis[indx]=wrappedThis[indx+1]; indx++; }
                         wrappedThis.length--;
                         return popped;
                       }
                 };
  getWrapper.wrappers.push({ wthis : wrappedThis, wprop : wrappedProp, wrapper :  newWrapper }); 
  return newWrapper;
};
getWrapper.wrappers = [];

现在只是一个小测试:

var myColl = new MsgObjCollection();
myColl.push({ source : 'Sent', message : 'hello to Jhon' });
myColl.push({ source : 'Received' , message : 'hi from Kate' });
myColl.push({ source : 'Sent', message : 'hello to Kate' });
myColl.push({ source : 'Received' , message : 'Reply from Jhon' });
myColl.push({ source : 'Received' , message : 'Ho, i forgot from Jhon' });

console.log('total number of messages : ' + myColl.count);
console.log('sent : ' + myColl.sent + '  Sent.count ' + myColl.Sent.count);
console.log('received : ' + myColl.received + '  Received.count ' + myColl.Received.count);
console.log('removing oldest sent message ');
var myLastSent = myColl.Sent.unshift();
console.log ('oldest sent message content : ' + myLastSent.message);
console.log('total number of messages : ' + myColl.count);
console.log('sent : ' + myColl.sent + '  Sent.count ' + myColl.Sent.count);
console.log('received : ' + myColl.received + '  Received.count ' + myColl.Received.count);

输出:>>

total number of messages : 5 
sent : 2  Sent.count 2 
received : 3  Received.count 3 
removing oldest sent message  
oldest sent message content : hello to Jhon
total number of messages : 4 
sent : 1  Sent.count 1 
received : 3  Received.count 3 

烦人的部分是这些视图属性不是数组,但是由于您不能重载 [] 运算符,因此您不能在原始数组上拥有完全透明的视图(即: myBox.Sent[i] 这将是i 个sent message ) 所以在某些时候您可能希望为某些操作动态创建数组。

于 2013-06-16T22:48:19.933 回答
0

有几种方法可以做到这一点。如果您只需要一个实例,最简单的一种是对象文字:

var MsgObjCollection = {
    _arr : [],
    push : function(val) {
        return this._arr.push(val);
    },

    Sent : {
        Count : function() {
           // ...
        }
    },

    // etc.
};

如果您需要多个实例,请使用构造函数,并向其prototype属性添加方法:

function MsgObjCollection() {
    this._arr = [];
}
MsgObjCollection.prototype.push = function(val) {
    return this._arr.push(val);
}
MsgObjCollection.prototype.get = function(index) {
    return this._arr[index];
}
// and so on...

// USAGE:
var collection = new MsgObjCollection();
collection.push('foo');
console.log(collection.get(0));
于 2013-06-16T19:03:53.140 回答