events.js 源码分析

1. 初始化
  1. // 使用
  2. this.ee = new EventEmitter();
  1. // 源码
  2. // 绑定this域,初始化 _events,_eventsCount和_maxListeners对象
  3. function EventEmitter() {
  4. EventEmitter.init.call(this);
  5. }
  6. EventEmitter.init = function() {
  7. if (
  8. this._events === undefined ||
  9. this._events === Object.getPrototypeOf(this)._events
  10. ) {
  11. this._events = Object.create(null);
  12. this._eventsCount = 0;
  13. }
  14. this._maxListeners = this._maxListeners || undefined;
  15. };
2.注册监听事件
  1. // 使用
  2. this.ee.on("message", function(text) {
  3. console.log(text);
  4. that.setState({
  5. text: text
  6. });
  7. });
  1. // 调用的是addListener
  2. EventEmitter.prototype.on = EventEmitter.prototype.addListener;
  3. EventEmitter.prototype.addListener = function addListener(type, listener) {
  4. // 注意最后一个参数为false
  5. return _addListener(this, type, listener, false);
  6. };
  7. function _addListener(target, type, listener, prepend) {
  8. var m;
  9. var events;
  10. var existing;
  11. // 判断是否是方法
  12. if (typeof listener !== "function") {
  13. throw ...
  14. }
  15. // this._events为单个EventEmitter对象中,注册事件的总对象,例如:{login:funcation(){xxxx},loginOut:funcation(){}}
  16. events = target._events;
  17. if (events === undefined) {
  18. events = target._events = Object.create(null);
  19. target._eventsCount = 0;
  20. } else {
  21. // To avoid recursion in the case that type === "newListener"! Before
  22. // adding it to the listeners, first emit "newListener".
  23. // 在添加监听时,判断是否有注册type='newListener'的监听,有的话,每_addListener便调用
  24. // newListener
  25. if (events.newListener !== undefined) {
  26. target.emit(
  27. "newListener",
  28. type,
  29. listener.listener ? listener.listener : listener
  30. );
  31. // Re-assign `events` because a newListener handler could have caused the
  32. // this._events to be assigned to a new object
  33. events = target._events;
  34. }
  35. // 获取是否已经注册过了
  36. existing = events[type];
  37. }
  38. if (existing === undefined) {
  39. // Optimize the case of one listener. Don't need the extra array object.
  40. // 没有注册过,赋值
  41. existing = events[type] = listener;
  42. ++target._eventsCount;
  43. } else {
  44. if (typeof existing === "function") {
  45. // Adding the second element, need to change to array.
  46. // 如果有相同的type,则将 新,旧listener封装成数组,并且根据prepend
  47. // 决定先后执行顺序
  48. existing = events[type] = prepend
  49. ? [listener, existing]
  50. : [existing, listener];
  51. // If we've already got an array, just append.
  52. } else if (prepend) {
  53. existing.unshift(listener);
  54. } else {
  55. existing.push(listener);
  56. }
  57. // Check for listener leak
  58. // 检查listener是否超过最大值,m初始化为10
  59. m = $getMaxListeners(target);
  60. if (m > 0 && existing.length > m && !existing.warned) {
  61. ...
  62. }
  63. }
  64. return target;
  65. }
  1. 获取EventEmitter对象events = target._events;

  2. events == null 进行判断

    1. true: 初始化 events = target._events = Object.create(null); target._eventsCount = 0;
    2. false:
      1. 判断是否存在newListener

        1. 存在: 调用target.emit("newListener",xxxx)
      2. existing = events[type];获取缓存数据,没有为undefined
  3. 判断existing === undefined

    1. true: 赋值existing = events[type] = listener; ++target._eventsCount;

      相当于给EventEmitter赋值{xxxx,'type':listener}

    2. false: 意味着注册了相同的type,将会生成{xxxx,'type':[listener1,listener2]}

      类似对象.ps: prepend == true 的时候,新listener,将会排列在数组的最前面.

  4. 检查listener是否超过最大值,初始化为10

  5. 返回target也就是当前EventEmitter对象.

3.发送事件
  1. // 使用
  2. this.ee.emit("message", "hello world 按钮1");
  1. EventEmitter.prototype.emit = function emit(type) {
  2. var args = [];
  3. // 获取除了type之外的参数,也就是传递参数
  4. for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
  5. var doError = type === "error";
  6. // this._events为单个EventEmitter对象中,注册事件的总对象,例如:{login:funcation(){xxxx},loginOut:funcation(){}}
  7. var events = this._events;
  8. // 进行error判断 暂时不关注
  9. ...
  10. // 获取到type对应的执行函数listener
  11. var handler = events[type];
  12. // 没有执行函数 return
  13. if (handler === undefined) return false;
  14. // 根据是funcation还是数组,分别执行对应的调用函数方法
  15. if (typeof handler === "function") {
  16. ReflectApply(handler, this, args);
  17. } else {
  18. var len = handler.length;
  19. var listeners = arrayClone(handler, len);
  20. for (var i = 0; i < len; ++i) ReflectApply(listeners[i], this, args);
  21. }
  22. return true;
  23. };
  1. // 兼容处理
  2. var ReflectApply =
  3. R && typeof R.apply === "function"
  4. ? R.apply
  5. : function ReflectApply(target, receiver, args) {
  6. return Function.prototype.apply.call(target, receiver, args);
  7. };
  1. 通过arguments获取到除type外其他的传入参数,用于最后调用方法时传递给执行函数
  2. 错误处理
  3. 获取到handler,并判断是否为undefined
  4. 根据获取到的handler,判断是funcation还是数组,分别执行对应的调用函数方法.
4.once解析
  1. // 使用
  2. // 注册
  3. this.ee.once("message3", function(text) {
  4. alert(text);
  5. });
  6. // 发送
  7. this.ee.emit("message3", "按钮3,只调用一次this.ee.once");
  1. EventEmitter.prototype.once = function once(type, listener) {
  2. if (typeof listener !== "function") {
  3. throw ....
  4. }
  5. // 包装 listener
  6. this.on(type, _onceWrap(this, type, listener));
  7. return this;
  8. };
  9. function _onceWrap(target, type, listener) {
  10. var state = {
  11. fired: false,
  12. wrapFn: undefined,
  13. target: target,
  14. type: type,
  15. listener: listener
  16. };
  17. // 将onceWrapper的this绑定为上面创建的state对象
  18. var wrapped = onceWrapper.bind(state);
  19. wrapped.listener = listener;
  20. state.wrapFn = wrapped;
  21. return wrapped;
  22. }
  23. function onceWrapper() {
  24. var args = [];
  25. for (var i = 0; i < arguments.length; i++) args.push(arguments[i]);
  26. if (!this.fired) {
  27. // 判断是否使用过,没使用过的话先删除type--onceWrapper
  28. this.target.removeListener(this.type, this.wrapFn);
  29. this.fired = true;
  30. // 执行listener,并且传递数据
  31. ReflectApply(this.listener, this.target, args);
  32. }
  33. }
  1. 包装执行函数listener

  2. 创建state对象,将onceWrapperthis绑定为state

  3. 调用发送事件emit后调用到onceWrapper方法,先删除EventEmitter对应的type:onceWrapper,

    然后执行state中的listener

  4. 再次发送事件则在EventEmitter找不到对应的type,实现了只能监听一次.

  • removeListener解析
  • removeAllListeners解析

events.js 源码分析的更多相关文章

  1. basket.js 源码分析

    basket.js 源码分析 一.前言 basket.js 可以用来加载js脚本并且保存到 LocalStorage 上,使我们可以更加精准地控制缓存,即使是在 http 缓存过期之后也可以使用.因此 ...

  2. Backbone.js源码分析(珍藏版)

    源码分析珍藏,方便下次阅读! // Backbone.js 0.9.2 // (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc. // Backbone ...

  3. Require.js 源码分析

    本文将简单介绍下个人对require.js的源码分析,简单分析实现原理 一.require加载资源的流程 require中,根据AMD(Asynchronous Module Definition)的 ...

  4. Vue.js 源码分析(二十八) 高级应用 transition组件 详解

    transition组件可以给任何元素和组件添加进入/离开过渡,但只能给单个组件实行过渡效果(多个元素可以用transition-group组件,下一节再讲),调用该内置组件时,可以传入如下特性: n ...

  5. Vue.js 源码分析(二十二) 指令篇 v-model指令详解

    Vue.js提供了v-model指令用于双向数据绑定,比如在输入框上使用时,输入的内容会事实映射到绑定的数据上,绑定的数据又可以显示在页面里,数据显示的过程是自动完成的. v-model本质上不过是语 ...

  6. Vue.js 源码分析(十六) 指令篇 v-on指令详解

    可以用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码,例如: <!DOCTYPE html> <html lang="en"& ...

  7. Vue.js 源码分析(十四) 基础篇 组件 自定义事件详解

    我们在开发组件时有时需要和父组件沟通,此时可以用自定义事件来实现 组件的事件分为自定义事件和原生事件,前者用于子组件给父组件发送消息的,后者用于在组件的根元素上直接监听一个原生事件,区别就是绑定原生事 ...

  8. Vue.js 源码分析(三十一) 高级应用 keep-alive 组件 详解

    当使用is特性切换不同的组件时,每次都会重新生成组件Vue实例并生成对应的VNode进行渲染,这样是比较花费性能的,而且切换重新显示时数据又会初始化,例如: <!DOCTYPE html> ...

  9. Vue.js 源码分析(三十) 高级应用 函数式组件 详解

    函数式组件比较特殊,也非常的灵活,它可以根据传入该组件的内容动态的渲染成任意想要的节点,在一些比较复杂的高级组件里用到,比如Vue-router里的<router-view>组件就是一个函 ...

随机推荐

  1. [实验吧](web)因缺思厅的绕过 源码审计绕过

    0x00 直接看源码吧 早上写了个注入fuzz的脚本,无聊回到实验吧的题目进行测试,发现了这道题 地址:http://ctf5.shiyanbar.com/web/pcat/index.php 分析如 ...

  2. 如何看待malloc产生内存碎片

    上代码直接研究: int main() { int *heap_d; int *heap_e; int *heap_f; heap_d = (int *)malloc(10); heap_e = (i ...

  3. Android Studio Gradle project sync failed

    使用Android Studio 1.1.0创建新项目后,运行报以下错: Error:Unable to start the daemon process. This problem might be ...

  4. 什么是BASH?

    BASH是Bourne Again SHell的缩写.它由Steve Bourne编写,作为原始Bourne Shell(由/ bin / sh表示)的替代品.它结合了原始版本的Bourne Shel ...

  5. Ribbon和Feign的区别?

    1.Ribbon都是调用其他服务的,但方式不同.2.启动类注解不同,Ribbon是@RibbonClient feign的是@EnableFeignClients3.服务指定的位置不同,Ribbon是 ...

  6. 解释一下Spring AOP里面的几个名词?

    (1)切面(Aspect):被抽取的公共模块,可能会横切多个对象.在Spring AOP中,切面可以使用通用类(基于模式的风格)或者在普通类中以@AspectJ注解来实现. (2)连接点(Join p ...

  7. Oracle入门基础(十三)一一java调用oracle存储过程

    package demo; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.ResultS ...

  8. Centos最小化安装

    1.选择最小安装: 2.选择相应的安装包 老男孩提示: 1.根据经验,选择安装包时应该按最小化原则,即不需要的或者不确定是否需要的就不安装,这样可以最大程度上确保系统安全. 2.如果安装过程落了部分包 ...

  9. ubuntu sublime text3 python 配置 sublime text3 python 配置

    ubuntu sublime text3 python 配置     1.安装sublime text 3 安装过程非常简单,在terminal中输入: sudo add-apt-repository ...

  10. 用纯RUST手撸一个开源流媒体服务(RTMP/HTTPFLV/HLS)XIU

    作者工作目前在音视频流媒体行业,用了大概一年的业余时间学习Rust,并且实现了一个简单的音视频流媒体服务,虽然据说Rust已经连续多年被评为最受程序员喜欢的语言,但是在国内还是比较冷门,作者比较看好R ...