Coder Social home page Coder Social logo

events's Introduction

Events


spm package Build Status Coverage Status

提供基本的事件添加、移除和触发功能。


使用说明

使用 Events 有两种方式,一种是直接实例化:

define(function(require) {
    var Events = require('events');

    var object = new Events();
    object.on('expand', function() {
        alert('expanded');
    });

    object.trigger('expand');
});

另一种是将 Events 混入(mix-in)到其他类中:

define(function(require) {
    var Events = require('events');

    function Dog() {
    }
    Events.mixTo(Dog);

    Dog.prototype.sleep = function() {
        this.trigger('sleep');
    };

    var dog = new Dog();
    dog.on('sleep', function() {
        alert('狗狗睡得好香呀');
    });

    dog.sleep();
});

上面的例子已经展现了 on, trigger mixTo 等方法的基本用法,下面详细阐述所有 API 。

on object.on(event, callback, [context])

给对象添加事件回调函数。

可以传入第三个参数 context 来改变回调函数调用时的 this 值:

post.on('saved', callback, that);

注意event 参数有个特殊取值:all 对象上触发任何事件,都会触发 all 事件的回调函数,传给 all 事件回调函数的第一个参数是事件名。例如,下面的代码可以将一个对象上的所有事件代理到另一个对象上:

proxy.on('all', function(eventName) {
    object.trigger(eventName);
});

off object.off([event], [callback], [context])

从对象上移除事件回调函数。三个参数都是可选的,当省略某个参数时,表示取该参数的所有值。例子:

// 移除 change 事件上名为 onChange 的回调函数
object.off('change', onChange);

// 移除 change 事件的所有回调函数
object.off('change');

// 移除所有事件上名为 onChange 的回调函数
object.off(null, onChange);

// 移除上下文为 context 的所有事件的所有回调函数
object.off(null, null, context);

// 移除 object 对象上所有事件的所有回调函数
object.off();

trigger/emit object.trigger(event, [*args])

触发一个或多个事件(用空格分隔)。*args 参数会依次传给回调函数。

obj.on('event', function(arg1, arg2) {
  // your code
});

obj.trigger('event', arg1, arg2);

trigger 的返回值是一个布尔值,会根据所有 callback 的执行情况返回。只要有一个 callback 返回 false,trigger 就会返回 false。

obj.on('event', function() {
  // do sth.
});
obj.on('event', function() {
  // do sth.
  return false;
});
obj.on('event', function() {
  // do sth
});

obj.trigger('event'); // return false

注意onoffevent 参数也可以表示多个事件(用空格分隔),比如:

var obj = new Events();

obj.on('x y', fn);

// 等价:
obj.on('x').on('y');

mixTo Events.mixTo(receiver)

Events 的原型方法混入到指定对象或函数原型中。

问题讨论

  • handler 的异常处理 #1

性能对比

:最开始,该模块的主要代码直接来自 Backbone.Events. 后来发现 Backbone 的代码实现有较大的改进空间,因此将内部的数据结构从链表改成了数组,重构后大幅度提升了性能。目前 Backbone 已合并 Arale 的代码:

events's People

Contributors

afc163 avatar antife-yinyue avatar army8735 avatar bitdeli-chef avatar fool2fish avatar hotoo avatar leoner avatar lepture avatar lianqin7 avatar lifesinger avatar lizzie avatar popomore avatar yiminghe avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

events's Issues

Events 不应该提供 mixTo 方法。

  1. mixTo 应该由第三方 mix 模块提供。
  2. Events.mixTo 方法不应该将 trigger 方法暴露。trigger 方法应该只提供给内部使用。
define("ModuleA", functioin(){
  var ModuleA = function(){};
  Events.mixTo(ModuleA);

  module.exports = ModuleA;
});

var a = new ModuleA();
// 触发事件是对象内部逻辑、状态,a 对象不应该拥有 `trigger` 方法。

是否有必要添加这样一种事件类别

类似jQuery对domready的事件处理形式。当这个事件被触发后,再绑定该事件,处理函数会直接被触发。

比如新建一个类的实例,实现需要异步加载资源(有cache),之后才能用,这时候会有一个ready事件。
但是,同一个页面周期,再次new一个实例,这时候,因为cache,资源不在需要异步加载,在新建的代码片段中,就会直接trigger('ready')。那么在新建这个实例的代码后,再on('ready'),就不会执行了。

虽然有各种办法能解决这问题,但是觉得这算个有必要的功能。

关于回调函数截取效率的问题

events.js 中使用循环截取传入的回调函数:

// Fill up `rest` with the callback arguments.  Since we're only copying
// the tail of `arguments`, a loop is much faster than Array#slice.
for (i = 1, len = arguments.length; i < len; i++) {
  rest[i - 1] = arguments[i]
}

使用 Array.prototype.slice 会使代码更精简,为什么说循环的效率会更高?

注册事件时可否支持defer回调

考虑下面的情景:

  • 一段向后端保存数据代码,监听到某个event后再执行, 但希望在callback list的最后执行。
  • 一些render功能代码,也会希望在监听某个event时,被最后执行

这样defer一下这个callback的apply,就可以简单实现了。

当然,注册事件的时候可以直接挂载一个带有defer特性的函数, 但是觉得如果能配置会更方便一些。
例如on的时候传个参数{defer:true}.


注:如果传个config形式的参数,就可以附加更多的特性了,例如:

{
    defer : true,   //推迟执行
    once : true,   //只执行一次
    onerror : DEFAULT_ERROR_HANDLE,
    blah blah....
}

这样符合arale的理念吗?

绑定多事件时无法获得触发的事件名

xx.on('event1 event2 event3', handle);

handle 里无法得知是哪个事件触发的handle

一个简单的方式: 对handle追加最后一个参数 即 eventType

xx.on('event1 event2 event3', function(){
    var eventType = arguments[arguments.length - 1];
});

trigger('all')的重复回调想法

昨天话题的延伸。

如果有一个callback,同时绑定了某个事件(如a)和all,那么触发a或者all的时候,是否也应该只执行一次?

var e = new Events();
function cb(){console.log(arguments)}
e.on('a', cb)
e.on('all', cb)
//分别触发
e.trigger('all')
e.trigger('a')

IE6-8 不支持Object.keys Array.forEach

copy了ieBetter.js的部分代码解决了此问题
https://github.com/zhangxinxu/ieBetter.js/blob/master/ieBetter.js
https://github.com/zhangxinxu/ieBetter.js

if (!Object.keys) {
    Object.keys = function(o) {
        if (o !== Object(o)) {
            throw new TypeError('Object.keys called on a non-object');
        }
        var k=[], p;
        for (p in o) {
            if (Object.prototype.hasOwnProperty.call(o,p)) {
                k.push(p);
            }
        }
        return k;
    };
}

  if (typeof Array.prototype.forEach != "function") {
    Array.prototype.forEach = function (fn, scope) {
        var i, len;
        for (i = 0, len = this.length; i < len; ++i) {
            if (i in this) {
                fn.call(scope, this[i], i, this);
            }
        }
           };
}

element 想实例化 以后 再生成

我根据需要new 5个Line 组件(同样使用 aralejs-widget),我本来打算在Line 组件里面,setup里面生成 画line的html,但是需要在new的时候就要输入element的名字,否则不能使用event 的方法,

能不能 动态指定 element?

代码应该有兼容问题 IE8- 直接报错吧

if (isFunction(receiver)) {
for (var key in proto) {
if (proto.hasOwnProperty(key)) {
receiver.prototype[key] = proto[key]
}
}
Object.keys(proto).forEach(function(key) {
receiver.prototype[key] = proto[key]
})
}
是不是考虑 try-catch 一下
try{
Object.keys(proto).forEach(function(key) {
receiver.prototype[key] = proto[key]
})
}catch(e){
for (var key in proto) {
if (proto.hasOwnProperty(key)) {
receiver.prototype[key] = proto[key]
}
}
}

trigger('all') 所绑定的事件会调用两次

看到 trigger 的源码中这些写:

// Execute event callbacks.
returned = triggerEvents(list, rest, this) && returned

// Execute "all" callbacks.
returned = triggerEvents(all, [event].concat(rest), this) && returned

那么使用 object.trigger('all') 的时候,对应的回调应该会被调用两次,只是第二个回调的参数中多了一个事件名。

请问这样的设计是出于什么考虑,还是我的理解有误。

关于事件重复绑定及解绑

var evt = new Events();

var name = "event-name";
function handler(){}

evt.on(name, handler); // 绑定第一次
evt.on(name, handler); // 绑定第二次

evt.emit(name); // handler 执行两次

evt.off(name, handler); // 解绑一次
evt.emit(name); // handler 不执行。

这个逻辑是否不正常?解绑时,在找到第一个 handler 后是否应该 break ?

关于trigger时异常处理的建议

Hi,aralers :)
最近考虑使用arale的events,但发现在trigger的时候,对for循环中的apply没有异常捕获。这样的话,如果注册上来的某个callback出现异常,那么callback list中后续的所有回调函数都将得不到执行。

个人认为这样不够合理,所以有后续建议,仅为抛砖。apply放入try catch中,出现异常立即处理,考虑以下处理办法

  1. log输出warn或error,再移除异常callback,也可考虑回调处理error的callback
  2. catch 到error后,再defer抛出,这样至少不会影响后续执行,同时不丢失error信息

注:原来在写页游时遇到的一个情景:我们游戏的战斗逻辑等各种复杂逻辑是通过events来处理的,这样效果很好,当所有逻辑与渲染开发完毕后,通过event的形式挂载了音效模块,即对各种战斗事件挂载播放不同音乐。但如果音乐播放出现异常,基于event的其他游戏逻辑不会受到影响。
注:本来想写个test验证一下,但是tests中的runner跑不起来,http://aralejs.org/tools/seajs-helper.jshttp://aralejs.org/tools/jasmine-runner.js 都是404。spm用的不是很熟,是不是我用错了。

mixTo 的一点问题

先贴出代码

Events.mixTo = function(receiver) {
    receiver = receiver.prototype || receiver;
    var proto = Events.prototype;
    for (var p in proto) {
        if (proto.hasOwnProperty(p)) {
            receiver[p] = proto[p];
        }
    }
};

有一个比较少见(甚至可以说是错误的)代码可能导致它失效。

var foo = {};
foo.prototype = {a:1};

Events.mixTo(foo);
// foo.on(...) 就挂了

各位大神觉得是否需要修复这个 bug 呢,用一个 type() 来判断而不是 prototype

增加 once

是否需要增加这个 api

var e = new Events();
e.once('event', callback);
e.trigger('event');
e.trigger('event'); // 不执行

别名 emit

添加一个别名 emit,等效于 trigger

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.