在JavaScript中结合原型和“类”继承

时间:2013-06-21 17:13:18

标签: javascript inheritance prototype

在我的JavaScript应用程序中,我有几种对象,包括QueryDocumentPosting。文档包含一堆关于文档的元数据(标题,摘要等); a Posting表示某些查询在某个等级检索的某些文档。可以通过多个查询检索相同的文档,因此可能有多个与其关联的过帐。

该应用程序有两个相关视图:一个显示给定查询的所有过帐,另一个显示所有文档。由于用户可以以相同的方式与两个视图交互,因此我决定通过使Document实例成为一个或多个Posting实例的原型来使用原型继承。这样,Posting继承了Document中的所有元数据,只添加了自己的Query引用和排名值。像魅力一样。

除了数据(查询,排名)之外,发布还包含一些行为。我想分解这种行为,以便我没有创建数以千计的相同方法。对于Document,我只是将我的所有功能都移到了Document.prototype。但是在Postings的情况下,我不能这样做,因为每个Posting都有一个不同的原型(它各自的Document)。虽然我可以把Posting使用的一些方法放到Document.prototype中并以这种方式得到它们,但是有些方法是多态的,需要以不同的方式实现Document和Posting。

我的问题是:如果我在Posting中使用原型继承来携带数据,我是否可以以某种方式分解行为以便我重用相同的方法函数实例对于我的所有Posting实例,而不是每次创建新的Posting时都创建一批新的方法?

function Document(id, title) {
    this.id = id;
    this.title = title;
}

// Here prototype is used to factor out behavior
Document.prototype.document = function() { return this; }
Document.prototype.toString = function() { return 'Document [' + this.id + ']: "' + this.title + '"'; }


function Posting(query, rank) {
    this.query = query;
    this.rank = rank;

    // Can I move these two definitions out of the Posting instance to avoid
    // creating multiple copies of these functions as I construct 1000s of Postings?
    this.document = function() { return Object.getPrototypeOf(this); }
    this.toString = function() { return this.document().toString() + ' at rank ' + this.rank; }
}

function createPosting(query, document, rank) {
    // here prototype is used to share data
    Posting.prototype = document;
    var posting = new Posting(query, rank);
    return posting;
}

更新

我在上面的代码示例中犯了一个错误:继承的正确方法(如下面的评论中所指出的)是首先设置原型:Posting.prototype = document;我认为我的其余问题仍然有效: - )

2 个答案:

答案 0 :(得分:0)

为什么不在Posting的构造函数之外保存函数。

function Document(id, title) {
    this.id = id;
    this.title = title;
}

// Here prototype is used to factor out behavior
Document.prototype.document = function() { return this; }
Document.prototype.toString = function() { return 'Document [' + this.id + ']: "' + this.title + '"'; }

function PostingToString()
{
  return this.document().toString() + ' at rank ' + this.rank;
}
function PostingDocument ()
{
    return Object.getPrototypeOf(this);
}
function Posting(query, rank) {
    this.query = query;
    this.rank = rank;

    this.document = PostingDocument; 
    this.toString = PostingToString;
}


function createPosting(query, document, rank) {
    var posting = new Posting(query, rank);
    // here prototype is used to share data
    posting.prototype = document;
    return posting;
}

答案 1 :(得分:0)

很难在浏览器中测试Javascript的内存使用情况......正如Gene .bind()在评论中所指出的那样,该函数的副本也是如此,因此Parthik Gosar的解决方案并没有保存任何内存。此外,它仍然引用了在大多数浏览器中并未真正作为引用处理的函数。所以,即使没有bind()函数,这也会占用大量内存。

这种方法应该可以节省大量内存......

var staticFcts = {
    'document': {
        'document': function() { return this; },
        'posting': function() { return Object.getPrototypeOf(this); }
    },
    'toString': {
        'document': function() { return 'Document [' + this.id + ']: "' + this.title + '"'; },
        'posting': function() { return this.document().toString() + ' at rank ' + this.rank; }
    }   
};

var myOwnProxy = function(method) {  
    var c = staticFcts[method][this.typeOf].bind(this);
    return c();
}



function Document(id, title) {
    this.id = id;
    this.title = title;
}

Document.prototype.document = function() { var p = myOwnProxy.bind(this,'document');     return p.call(); };
Document.prototype.toString = function() { var p =     myOwnProxy.bind(this,'toString');return p.call(); }; 
Document.prototype.typeOf = 'document'; // default type is 'document', this is necessary
// because there is no way in Javascript to determine the class name.
// Unfortunantely "Proxy" (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)  is not widely supported either.

function Posting(query, rank) {
    this.query = query;
    this.rank = rank;
}

function createPosting(query, document, rank) {
    Posting.prototype = document;
    var posting = new Posting(query, rank);
    posting.typeOf = 'posting'; //overwrite type

    return posting;
}


var docs = [];
var postings= [];

var now = new Date().getTime();

// memory and performance test
for($i=0;$i<100000;$i++) {
 var d = new Document(1,'document1');
 var p = createPosting('query1', d, 1);
    docs.push(d);
    postings.push(p);
}
console.log('needed time', ((new Date().getTime())-now)/1000 + ' sec');

function roughSizeOfObject( object ) {

    var objectList = [];
    var stack = [ object ];
    var bytes = 0;

    while ( stack.length ) {
        var value = stack.pop();

        if ( typeof value === 'boolean' ) {
            bytes += 4;
        }
        else if ( typeof value === 'string' ) {
            bytes += value.length * 2;
        }
        else if ( typeof value === 'number' ) {
            bytes += 8;
        }
        else if
        (
                typeof value === 'object'
            && objectList.indexOf( value ) === -1
        )
        {
            objectList.push( value );

            for( i in value ) {
                stack.push( value[ i ] );
        }
        }
    }
    return bytes;
}   

console.log('memory usage: ', roughSizeOfObject(docs) + roughSizeOfObject(postings));

西蒙