构建数组侦听器

时间:2013-11-30 20:46:22

标签: javascript

有没有办法将侦听器附加到由另一个进程构建的数组,以便当array.length达到某个值时,侦听器执行回调并触发相应的代码?

fooArray = [];
// another code adds objects to array

if (fooArray.length = 5) {
  // callback
} else {
  // continue checking fooArray.length
}

2 个答案:

答案 0 :(得分:3)

你必须使用自己的方法来改变数组

Array.prototype.myPush = function () {

    this.push.apply( this, arguments );
    if ( this._binder && this._binder.test.call( this ) ) {
        this._binder.callback.call( this );
    }

}

Array.prototype.bind = function (tester, callback) {
    this._binder = {
        test: tester,
        callback: callback
    };
}

var arr = [4, 6, 9, 20];
arr.bind( function () {
    return this.length >= 5;
}, function () {
    console.log( "Array has exceeded" );
} );

arr.myPush( 40 );

答案 1 :(得分:3)

嗯,是的,不是。没有办法以100%安全的方式执行,因为JavaScript不支持运算符重载,这意味着您无法修改数组索引运算符([])的行为。

但是,使用数组索引运算符添加的数组很少见。也就是说,这是一种非常罕见(但有效)的方法,可以将项添加到数组中:

var arr = [];
for( i=0; i<5; i++ ) arr[i] = i;

结果将是一个包含五个元素的数组。但更常见的是,您会看到:

var arr = [];
for( i=0; i<5; i++ ) arr.push( i );

现在我们可以合作了。因此,现在的解决方案是将push函数的原始功能与包含引发事件的其他功能结合起来。

有三种基本方法可以做到这一点。第一个是修改Array.prototype.push。我会引导你清除这种方法:导致极难追踪的缺陷的可能性很高。第二种是创建自己的数组类型,扩展JavaScript的内置Array类型。如果你要创建许多需要监听的数组,这将是最好的方法。最后一种方法只是修改单个数组,这是最少的工作,以及我将在这里描述的方法。

你可以通过任何这些方法获得所需的复杂功能,但我只会提供一个非常小的基础,允许你附加任意的侦听器,并且可以扩展为其他功能(删除侦听器,引发事件)不同的数组修改方法等。)。

首先,您需要一个数组和一个用于保存侦听器的属性:

var arr = [];
arr._listeners = [];

然后您可以相应地修改push方法:

arr.push = function() {
    var arr = this;
    Array.prototype.push.apply( arr, arguments );    
    arr._listeners.forEach( function(listener) {
        listener( { event: 'push', newLength: arr.length } );
    });
}

你可以在你想要的事件对象中放置任何东西(比如旧计数,添加元素,时间戳等)。

为了实现这一功能,您必须将此技术用于修改数组的Array的其他每种方法,例如popshift等。查看全部这里Array的方法:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/prototype#Methods

根据您的需求,您可以考虑其他技术。例如,如果你做了很多函数包装(就像我们上面用push所做的那样),你应该考虑某种面向方面编程(AOP)框架,如meld。你也可以很容易地写自己的。例如,我们可以这样重复上述工作:

function after( f, after ) {
    return function() {
        f.apply( this, arguments );
        after.apply( this, arguments );
    }
}

var arr = [];
// convenience function for broadcasting an event
arr._broadcast = function( o ) { 
    this._listeners.forEach( function(listener) {
        listener( o );
    }
}

arr.push = after( arr.push, function() {
    this._broadcast( { event: 'push', newLength: arr.length } );
});

这种方法的优点是可以更容易地扩展Array的其他方法:

arr.pop = after( arr.pop, function() {
    this._broadcast( { event: 'pop', newLength: arr.length } );
});

等等。

如果你需要能够创建许多这样的“广播”数组,你可以像前面提到的那样创建自己的自定义数组类型Array,但JavaScript提供了更多灵活的代码模式比传统的OOP重复使用。因此,您可以创建一个EventArray函数,而不是创建扩展Array的{​​{1}},这会奇怪地使该实例成为事件发射器:

Array.prototype

现在,只要你有一个阵列想要播放它发生的事情,你所要做的就是:

Array.prototype.makeBroadcaster = function() {

    this._listeners = this._listeners || [];
    this._broadcast = function( o ) { 
        this._listeners.forEach( function(listener) {
        listener( o );
    }
    this.registerListener = function( listener ) {
        this._listeners.push( listener );
    }

    this.push = after( this.push, function() {
        this._broadcast( { event: 'push', newLength: arr.length } );
    }
    this.pop = after( this.push, function() {
        this._broadcast( { event: 'push', newLength: arr.length } );
    }
    this.shift = after( this.push, function() {
        this._broadcast( { event: 'shift', newLength: arr.length } );
    }
    // etc....
}

一些结束语:

  • 正如我在帖子开头所提到的,如果您的代码使用数组索引运算符修改数组,则无法在其上引发事件。具体而言,可以使用数组索引运算符“增长”数组。换句话说,如果数组包含5个元素,并且执行var arr = []; arr.makeBroadcaster(); arr.addListener( function(evt) { console.log( "%s (new length: %d)", evt.event, evt.newLength ); } ); ,则数组中将包含10个元素(5-8为arr[9] = 'hello')。没有办法检测到这一点,但是以这种方式修改数组的情况非常罕见。

  • 您还可以使用undefined属性修改数组的长度。也就是说,如果你有一个十元素数组,并且你调用length,它将截断数组只有五个元素。可能通过覆盖arr.length = 5属性(使用length)来检测到这一点,但如果可能的话,这听起来真的很危险。再次,以这种方式截断数组在JavaScript中非常罕见。

  • 如果事件真的绝对必须100%准确,即使在上面的情况下,你最好的选择是能够创建自己的对象,它具有基本的数组函数,如Object.defineProperty和喜欢。但是,这将非常尴尬,因为你必须实现push之类的东西,而不是使用更方便的数组索引操作符。

这是一个展示整个行动的jsfiddle:http://jsfiddle.net/g68jC/

JavaScript不是很整洁吗?