条件初始化

条件初始化(也叫条件加载)是一种优化模式。当你知道某种条件在整个程序生命周期中都不会变化的时候,那么对这个条件的探测只做一次就很有意义。浏览器探测(或者特征检测)是一个典型的例子。

举例说明,当你探测到XMLHttpRequest被作为一个本地对象支持时,就知道浏览器不会在程序执行过程中改变这一情况,也不会出现突然需要去处理ActiveX对象的情况。当环境不发生变化的时候,你的代码就没有必要在需要在每次初始化XHR对象时探测一遍(并且得到同样的结果)。

另外一些可以从条件初始化中获益的场景是获得一个DOM元素的computed styles或者是绑定事件处理函数。大部分程序员在他们的编程生涯中都编写过事件绑定和取消绑定相关的组件,像下面的例子:

  1. // 优化前的代码
  2. var utils = {
  3. addListener: function (el, type, fn) {
  4. if (typeof window.addEventListener === 'function') {
  5. el.addEventListener(type, fn, false);
  6. } else if (typeof document.attachEvent === 'function') { // IE
  7. el.attachEvent('on' + type, fn);
  8. } else { // 老的浏览器
  9. el['on' + type] = fn;
  10. }
  11. },
  12. removeListener: function (el, type, fn) {
  13. // 和上面很类似的代码……
  14. }
  15. };

这段代码的问题就是效率不高。每当你执行utils.addListener()或者utils.removeListener()时,同样的检查都会被重复执行。

如果使用条件初始化,那么浏览器探测的工作只需要在初始化代码的时候执行一次。在初始化的时候,代码探测一次环境,然后重新定义这个函数在剩下来的程序生命周期中应该怎样工作。下面是一个例子,看看如何达到这个目的:

  1. // 优化后的代码
  2. // 接口
  3. var utils = {
  4. addListener: null,
  5. removeListener: null
  6. };
  7. // 实现
  8. if (typeof window.addEventListener === 'function') {
  9. utils.addListener = function (el, type, fn) {
  10. el.addEventListener(type, fn, false);
  11. };
  12. utils.removeListener = function (el, type, fn) {
  13. el.removeEventListener(type, fn, false);
  14. };
  15. } else if (typeof document.attachEvent === 'function') { // IE
  16. utils.addListener = function (el, type, fn) {
  17. el.attachEvent('on' + type, fn);
  18. };
  19. utils.removeListener = function (el, type, fn) {
  20. el.detachEvent('on' + type, fn);
  21. };
  22. } else { // older browsers
  23. utils.addListener = function (el, type, fn) {
  24. el['on' + type] = fn;
  25. };
  26. utils.removeListener = function (el, type, fn) {
  27. el['on' + type] = null;
  28. };
  29. }

说到这里,要特别提醒一下关于浏览器探测的事情。当你使用这个模式的时候,不要对浏览器特性过度假设。举个例子,如果你探测到浏览器不支持window.addEventListener时,不要假设这个浏览器是IE,也不要认为它不支持原生的XMLHttpRequest,虽然这个结论在整个浏览器历史上的某个时间点是正确的。当然,也有一些情况是可以放心地做一些特性假设的,比如.addEventListener.removeEventListerner,但是通常来讲,浏览器的特性在发生变化时都是独立的。最好的策略就是分别探测每个特性,然后使用条件初始化,使这种探测只做一次。