events.js 源码分析

1. 初始化
// 使用
this.ee = new EventEmitter();
// 源码
// 绑定this域,初始化 _events,_eventsCount和_maxListeners对象
function EventEmitter() {
EventEmitter.init.call(this);
}
EventEmitter.init = function() {
if (
this._events === undefined ||
this._events === Object.getPrototypeOf(this)._events
) {
this._events = Object.create(null);
this._eventsCount = 0;
} this._maxListeners = this._maxListeners || undefined;
};
2.注册监听事件
// 使用
this.ee.on("message", function(text) {
console.log(text);
that.setState({
text: text
});
});
// 调用的是addListener
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.addListener = function addListener(type, listener) {
// 注意最后一个参数为false
return _addListener(this, type, listener, false);
};
function _addListener(target, type, listener, prepend) {
var m;
var events;
var existing;
// 判断是否是方法
if (typeof listener !== "function") {
throw ...
} // this._events为单个EventEmitter对象中,注册事件的总对象,例如:{login:funcation(){xxxx},loginOut:funcation(){}}
events = target._events;
if (events === undefined) {
events = target._events = Object.create(null);
target._eventsCount = 0;
} else {
// To avoid recursion in the case that type === "newListener"! Before
// adding it to the listeners, first emit "newListener".
// 在添加监听时,判断是否有注册type='newListener'的监听,有的话,每_addListener便调用
// newListener
if (events.newListener !== undefined) {
target.emit(
"newListener",
type,
listener.listener ? listener.listener : listener
); // Re-assign `events` because a newListener handler could have caused the
// this._events to be assigned to a new object
events = target._events;
}
// 获取是否已经注册过了
existing = events[type];
} if (existing === undefined) {
// Optimize the case of one listener. Don't need the extra array object.
// 没有注册过,赋值
existing = events[type] = listener;
++target._eventsCount;
} else {
if (typeof existing === "function") {
// Adding the second element, need to change to array.
// 如果有相同的type,则将 新,旧listener封装成数组,并且根据prepend
// 决定先后执行顺序
existing = events[type] = prepend
? [listener, existing]
: [existing, listener];
// If we've already got an array, just append.
} else if (prepend) {
existing.unshift(listener);
} else {
existing.push(listener);
} // Check for listener leak
// 检查listener是否超过最大值,m初始化为10
m = $getMaxListeners(target);
if (m > 0 && existing.length > m && !existing.warned) {
...
}
}
return target;
}
  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.发送事件
// 使用
this.ee.emit("message", "hello world 按钮1");
EventEmitter.prototype.emit = function emit(type) {
var args = [];
// 获取除了type之外的参数,也就是传递参数
for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
var doError = type === "error";
// this._events为单个EventEmitter对象中,注册事件的总对象,例如:{login:funcation(){xxxx},loginOut:funcation(){}}
var events = this._events; // 进行error判断 暂时不关注
...
// 获取到type对应的执行函数listener
var handler = events[type];
// 没有执行函数 return
if (handler === undefined) return false; // 根据是funcation还是数组,分别执行对应的调用函数方法
if (typeof handler === "function") {
ReflectApply(handler, this, args);
} else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i) ReflectApply(listeners[i], this, args);
}
return true;
};
// 兼容处理
var ReflectApply =
R && typeof R.apply === "function"
? R.apply
: function ReflectApply(target, receiver, args) {
return Function.prototype.apply.call(target, receiver, args);
};
  1. 通过arguments获取到除type外其他的传入参数,用于最后调用方法时传递给执行函数
  2. 错误处理
  3. 获取到handler,并判断是否为undefined
  4. 根据获取到的handler,判断是funcation还是数组,分别执行对应的调用函数方法.
4.once解析
// 使用
// 注册
this.ee.once("message3", function(text) {
alert(text);
});
// 发送
this.ee.emit("message3", "按钮3,只调用一次this.ee.once");
EventEmitter.prototype.once = function once(type, listener) {
if (typeof listener !== "function") {
throw ....
}
// 包装 listener
this.on(type, _onceWrap(this, type, listener));
return this;
}; function _onceWrap(target, type, listener) {
var state = {
fired: false,
wrapFn: undefined,
target: target,
type: type,
listener: listener
};
// 将onceWrapper的this绑定为上面创建的state对象
var wrapped = onceWrapper.bind(state);
wrapped.listener = listener;
state.wrapFn = wrapped;
return wrapped;
}
function onceWrapper() {
var args = [];
for (var i = 0; i < arguments.length; i++) args.push(arguments[i]);
if (!this.fired) {
// 判断是否使用过,没使用过的话先删除type--onceWrapper
this.target.removeListener(this.type, this.wrapFn);
this.fired = true;
// 执行listener,并且传递数据
ReflectApply(this.listener, this.target, args);
}
}
  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. atoi atof atol

    在c语言中提供了把字符串转化为整数的函数,并没有提供把整数转化为字符串的函数 atoi是标准的库函数 itoa不是标准的库函数(在vs可编译,其它系统中未知) atol把一个字符串转化为long类型 ...

  2. flask 数据库一节笔记

    笔记一:os.path的用法:1. os.path.dirname(__file__)   返回当前脚本的执行路径,__file__为固定参数2. os.path.abspath(file)     ...

  3. idea如何打包项目,部署到linux后台运行

    服务器安装tomcat 上传好tomcat包 scp -r tomcat liwwww@111116.11222.101121.12111111:/opt/ linux下启动tomcat服务的命令   ...

  4. 【Java面试宝典】说说你对 Spring 的理解,非单例注入的原理?它的生命周期?循环注入的原理, aop 的实现原理,说说 aop 中的几个术语,它们是怎么相互工作的?

    AOP与IOC的概念(即spring的核心) IOC:Spring是开源框架,使用框架可以使我们减少工作量,提高工作效率并且它是分层结构,即相对应的层处理对应的业务逻辑,减少代码的耦合度.而sprin ...

  5. Spring由哪些模块组成?

    以下是Spring 框架的基本模块: Core module Bean module Context module Expression Language module JDBC module ORM ...

  6. 转:master公式(主方法)

    master公式(也称主方法)是利用分治策略来解决问题经常使用的时间复杂度的分析方法,(补充:分治策略的递归解法还有两个常用的方法叫做代入法和递归树法),众所众知,分治策略中使用递归来求解问题分为三步 ...

  7. java-doc注释详解

    注释的分类 // 注释一行/* ...... */ 注释若干行/** ...... */ 注释若干行,并写入 javadoc 文档 列子 /** * show 方法的简述. * <p>sh ...

  8. Spring AOP and AspectJ AOP 有什么区别?

    Spring AOP 基于动态代理方式实现:AspectJ 基于静态代理方式实现.Spring AOP 仅支持方法级别的 PointCut:提供了完全的 AOP 支持,它还支持属性级别的 PointC ...

  9. scanf()函数的原理

    最近使用scanf发现了自己对scanf函数还是不太了解,主要出现在无意中出现的一个错误: scanf正确的写法是,scanf中以什么格式输入变量,则变量的类型就应该是什么格式,如下面scanf输入到 ...

  10. STM32 中的 assert_param 函数

    在学STM32的时候函数assert_param出现的几率非常大,上网搜索一下,网上一般解释断言机制,做为程序开发调试阶段时使用. 断言机制函数assert_param我们在分析库函数的时候,几乎每一 ...