在JavaScript中,我想在长时间运行的应用程序中实例化一个对象。该对象应允许自己使用mixin进行扩充。有一些设计限制:


混合可能会覆盖现有方法
混合可能提供新方法
给定的mixin方法应该能够引用父类,从而可以将跨越多个mixin的同名行为通过管道传递到一起
从对象中删除混合源应该比添加混合源更容易
我想将mixins推入对象。也就是说,我希望不要包装/装饰该对象,因为过时的对象保存了对原始对象的引用。


在JS中,mixin通常将其方法直接复制到对象上。如果您不想轻易取消新行为,那就很好。这个想法是,由于应用程序可以无限期地运行,因此这些行为应随时易于添加或删除,而对对象进行添加行为以使其添加行为并不会使其日后变得整洁而容易。

我宽松地指“ mixins”。其主要思想是对象可以插入或拔出可以通过管道传输在一起的行为。该机制本身不太重要。可能熟悉Rack的一些人知道这很有用。

var tim = new Person('Tim'); //at 12:00pm
tim.eat()                    //at 12:00pm -- native Person behavior
tim.addBehavior(husband)     //at 12:00pm
tim.kissWife()               //at 12:00pm -- husband behavior
tim.addBehavior(father)      //at 12:00pm
tim.addBehavior(hungry)      //at 12:00pm -- augments the native eat behavior
tim.addBehavior(bowler)      //at 5:00pm
tim.bowl()                   //at 5:00pm
tim.performDailyDuties()     //at 5:00pm -- includes husband and father duties
tim.removeBehavior(bowler)   //at 5:00pm -- easily remove behavior
tim.bowl()                   //at 5:01pm -- error!
tim.kissWife()               //at 5:01pm


我不想...

var husbandTim = new Husband(tim)
var bowlerTim  = new Bowler(husbandTim)


...因为这样做很难消除一种特定的行为。另外,所有引用tim的地方该怎么办?那些地方不会意识到新的行为。

不幸的是,JS没有提供我所知道的任何东西来简化此过程。 ES6将提供允许这样做的代理,但我想知道是否会错过一种更简单的方法。

什么样的设计模式或机制可以使添加插件行为变得容易,而以后又可以轻松删除它们呢?是否有任何框架在这些方面做些事情?

最佳答案

我不久前创建了一个名为Uberproto的ES5继承库,该库确实允许您将mixins添加到对象并调用覆盖的方法。例如:

var PersonObject = {
    init : function(name) {
        this.name = name;
    },

    fullName : function() {
        return this.name;
    }
};

Proto.mixin({
    fullName : function() {
        return 'My name is: ' + this._super();
    }
}, PersonObject);

// Create a plain object without calling the constructor
var instance = Object.create(PersonObject);
instance.name = 'Dude';
console.log(instance.fullName()); // 'My name is: Dude'


我发现如果您仔细地为它创建mixin,它实际上会很好地工作。我从不需要删除mixin,但是它应该像在添加原始方法之前存储对原始方法的引用一样简单:

var PersonObject = {
    init : function(name) {
        this.name = name;
    },

    fullName : function() {
        return this.name;
    }
};

var backup = {
    fullName: PersonObject.fullName
};

Proto.mixin({
    fullName : function() {
        return 'My name is: ' + this._super();
    }
}, PersonObject);

// Create a plain object without calling the constructor
var instance = Object.create(PersonObject);
instance.name = 'Dude';
console.log(instance.fullName()); // 'My name is: Dude'

// Restore old mixin
PersonObject.fullName = backup.fullName;
var instance = Object.create(PersonObject);
instance.name = 'Dave';
console.log(instance.fullName()); // 'Dave'


因此,您所需要做的就是将该功能包装到更通用的东西中。

09-25 17:02
查看更多