特定对象实例的自定义函数 - 我有什么可能性



所以我目前正在构建一个相当大的框架,用于许多Web项目。我很想得到一些建议或最佳实践,以适应以下情况。

我在网络应用程序中有不同的章节。一次只能看到一章。

第一次访问章节时调用函数createView()。它通过车把模板创建视图,插入 XML 文档中的所有副本(每种语言都有一个 XML),并通过 start() 函数开始章节。 每当再次访问章节时,都会调用start()。它只是将视图插入 DOM 并启用所有功能,如点击事件、其他模块(画廊、滑块等)等。

var Chapter = function(name) {
    this.name = name;
    this.createView();
};
Chapter.prototype.createView = function() {
    var self = this;
    
    // get handlebars template
    this.template = config.app.getTemplate('chapters/'+self.name).then(function(hbs) {
        
        // compile hbs template, insert data here - if there is any
        var html = hbs();
        
        // save jQuery collection of HTML as view
        self.view = $(html);
        
        // insert copy from XML doc
        self.view.find('[data-text]').each(function() {
            var theID = '#'+$(this).attr('data-text');
            var theText = config.xml.find(theID).text();
            $(this).html(theText);
        });
        
        // start the experience
        self.start();
        
    });
};
Chapter.prototype.start = function() {
    
    // stop active experience
    if(config.experiences.active) {
        config.experiences.active.stop();
    }
    
    // scroll to top
    $(window).scrollTop(0);
    
    // save this chapter as the active one
    config.experiences.active = this;
    
    // insert view into DOM
    this.view.appendTo($('main'));
    
    // enable functionality
    this.initModules();
    this.initNavigation();
    this.initLocking();
    
};

现在,Chapter的每个实例都需要一些自定义函数,因为每个章节在很多项目中看起来都不同。但我不确定这是否是最好的方法:

initCustoms中使用 if 的 lof 。

Chapter.prototype.start = function() {
    ...
    this.initCustoms();
};
Chapter.prototype.initCustoms = function() {
    if(this.name === 'Fire and Ice') {
        // do abc
    } else if(this.name === 'Second Sons') {
        // do xyz
    }
};

那么,添加一堆if-else语句真的是要走的路吗?好像是这样...脏。我也虽然:

为自定义章节创建一个对象并继承其原型

var FireAndIce = function() {
    // do abc
}
FireAndIce.prototype = Chapter.prototype;
FireAndIce.prototype.constructor = Chapter;
Chapter.prototype.start = function() {
    ...
    this.initCustoms();
};
Chapter.prototype.initCustoms = function() {
    if(this.name === 'Fire and Ice') {
        // inherit functionality of FireAndIce. Can't post it here as I have not been able to do this properly.
    }
    ...
};

但这只会导致继承、更改其他实例或主Chapter原型的问题。我不想将FireAndIce的所有功能复制到实例中,并且根据我对这个主题的研究,我无法正确地从一个原型(FireAndIce)继承到另一个原型(Chapter)的实例。

另外,只是出于好奇,下面这个主意是个坏主意吗?

通过 jQuery 创建自定义start事件,我可以根据需要绑定任意数量的自定义处理程序

Chapter.prototype.start = function() {
    ...
    this.initCustoms();
    this.trigger('start');
};
Chapter.prototype.initCustoms = function() {
    if(this.name === 'Fire and Ice') {
        this.on('start', function() {
            // do abc
        });
    }
    ...
};

撇开我为什么要这样做的原因不谈,这有什么问题吗?我有点喜欢为每个章节设置一个startstop事件的想法,我可以从任何地方绑定其他功能。

提前感谢您的建议。

几个选项:

只需为他们分配一个功能

听起来好像只有几个Chapter实例,而且每一章都是一次性的(例如,FireAndIce章只有一个副本)。在这种情况下,您可以只创建章节实例,然后在创建它们后分配它们的initCustoms函数:

var fireAndIce = new Chapter();
fireAndIce.initCustoms = function() {
    // stuff for Fire and Ice
};
var secondSons = new Chapter();
secondSons.initCustoms = function() {
    // stuff for Second Sons
};
// ...

使用原型继承函数和构造函数

但是,如果您想使用继承来执行此操作,则如下所示:

function Chapter() {
}
Chapter.prototype.setup = function() {
    // ...chapter common stuff
};
function FireAndIce() {
    Chapter.apply(this, arguments); // Chain to parent constructor
    // ...fire and ice stuff
}
FireAndIce.prototype = Object.create(Chapter.prototype);
FireAndIce.prototype.constructor = FireAndIce
FireAndIce.prototype.initCustoms = function() {
    // ...Fire and Ice custom stuff
};
function SecondSons() {
    Chapter.apply(this, arguments); // Chain to parent constructor
    // ...Second Sons stuff
}
SecondSons.prototype = Object.create(Chapter.prototype);
SecondSons.prototype.constructor = SecondSons
SecondSons.prototype.initCustoms = function() {
    // ...Second Sons custom stuff
};

这样,FireAndIce实例共享其原型上的initCustoms,并且还共享Chapter原型上的所有内容,因为Chapter.prototypeFireAndIce.prototype对象背后的原型。(同样适用于SecondSons

请注意,如果派生函数(FireAndIceSecondSons)和Chapter具有不同的参数列表,则无需将所有参数从派生函数传递给Chapter,只需传递适当的参数:

Chapter.call(this, the, appropriate, args, go, here);

使用不带构造函数的原型继承

有些人更喜欢使用没有构造函数的原型继承,因此不使用new。这也是一种选择:

var ChapterProto = {
    setup: function() {
        // ...common chapter stuff...
    }
};
function createChapter() {
    var rv = Object.create(ChapterProto);
    // ...common chapter stuff...
    return rv;
}
var FireAndIceProto = Object.create(ChapterProto);
FireAndIceProto.initCustoms = function() {
    // ...Fire and Ice custom stuff...
};
function createFireAndIce() {
    var rv = Object.create(FireAndIceProto);
    createChapter.apply(rv, arguments);
    // ...Fire and Ice stuff...
    return rv;
}
var SecondSonsProto = Object.create(SecondSonsProto);
SecondSonsProto.initCustoms = function() {
    // ...Second Sons custom stuff...
};
function createSecondSons() {
    var rv = Object.create(SecondSonsProto);
    createChapter.apply(rv, arguments);
    // ...Second Sons stuff...
    return rv;
}

同样,如果参数列表不同,您可以使用.call而不是.apply

结论

对于几个对象,我更喜欢在创建实例后只为实例分配函数。代码更少,更简单。

但是对于对象(小写意义上),我将使用原型继承和上述构造函数(但其他一些人会在没有构造函数的情况下使用它)。


上面使用Object.create,这是ES5的新功能(现在大约五年了)。如果你需要支持非常旧的引擎,你可以填充上面使用的单参数版本(第二个参数不能在 ES3 引擎上填充):

if (!Object.create) {
    Object.create = function(proto, props) {
        if (props) {
            throw "The second argument to Object.create cannot be polyfilled.";
        }
        function ctor() {
        }
        ctor.prototype = proto;
        return new ctor();
    };
}

最新更新