Web 技术研究所

我一直坚信着,Web 将会成为未来应用程序的主流

再谈 JavaScript 的事件封装

  封装事件模型最大的障碍在于存储。当我们给一个对象绑定一组事件时,这一组事件应该存到哪儿呢?在出现 Set、Map 及其 Weak 版本之前,两种常见的解决方案是对象属性存储和闭包存储。我觉得前者会暴露私有数据而一直选择后者,然而最近被自己挖的坑给坑了。
  虽然把事件保存到对象属性上的做法很难看,但它至少是针对每个对象的。而且相关的方法可以继承而来甚至写成静态方法。比如 function on(base, name, handler) {
  base.events = base.evens || {};
  base.events[name] = base.events[name] || [];
  base.events[name].push(handler);
}

var a = {};

on(a, 'eventName', function() {
  // Handler
});
  这个方法造成的问题是显而易见的。比如上面例子中使用了 events 这个属性来保存事件列表。然而这个属性名是无法确保不会冲突的,或者可能在 for in 遍历的时候暴露出来(IE8- 无法将 enumerable 置否),所以这种做法具有一定危险性。
  上面这个方法的问题这么明显,所以我通常是使用另一种方法,那就说直接给实例加特技,即闭包存储。在定义实例上的事件相关方法时候在其函数作用域中注入一个用于存储的堆变量。比如 function duang(base) {
  var events = {};
  base.on = function(name, handler) {
    events[name] = events[name] || [];
    events[name].push(handler);
  };
}

var a = {};

a = duang(a); <!-- 加特技

a.on('eventName', function() {
  // Handler
});
  如果支持 ES5 的话(IE9+)还可以把 on 方法定义为不可枚举的,避免 for-in 时的问题。这个做法我用了很长一段时间,直到最近才意识到这个做法的坑。由于是在每个实例上绑定,如果这个实例被作为其它构造器的原型,并且那个构造器没有对其实例加特技,那么事件相关的方法就会被继承到实例上,造成所有实例的事件都绑定在一起的混乱情况。
  要想在 ES5 或更低版本的环境完美实现时间模型是很困难的。唯一在理论上完美的是用一个时间复杂度 O(n) 的方式去模拟一个 Map,然而性能坑定是要跪的,根本无法用在实际场景中。
  我现在觉得如果支持 ES5 的话在对象上添加一个不可枚举的键来存事件才是最佳做法,只是对于 IE8- 这种不支持 ES5 的环境目前确实想不到什么解决方案。
网名:
34.203.245.*
电子邮箱:
仅用于接收通知
提交 悄悄的告诉你,Ctrl+Enter 可以提交哦
神奇海螺
[查看全部主题]
各类Web技术问题讨论区
发起新主题
本模块采用即时聊天邮件通知的模式
让钛合金F5成为历史吧!
次碳酸钴的技术博客,文章原创,转载请保留原文链接 ^_^