更优雅的兼容

这和什么比较像?

事实上,如果你了解JavaScript对象系统的机理,你就可以类比:这不就是原型嘛!原型系统就是利用了这种跌落——寻找某个成员,如果它在这个对象里定义了,就返回之;否则沿着原型链向上搜(没错,这次是向上的),如此重复,直到真的连原型链都到头的时候,返回个undefined

说做就做!这里同样用addEvent为例。首先,我们定义一个空驱动,它里面什么都不包含:

var nullDriver = {}

然后,就是创建个对象,并且把原型链指向它。在ECMA V5时代,我们可以用Object.create,可惜,现在还有N多老客户端(否则做什么兼容啊),所以自己craft个函数:

var derive = Object.create ? Object.create: function() {
    var T = function() {};
    return function(obj) {
        T.prototype = obj;
        return new T
    }
}()

这个用法你可能会觉得很诡异,但它工作起来一点问题没有,速度也不慢——能达到Object.create的一半。我们就用这个derive开动:

var dhtmlDriver = derive(nullDriver);
var dhtmlDriverBugfix = derive(dhtmlDriver);

这里的bugfix是针对一些“bug”和特殊情况定义的特别Driver。这里你可以忽略它。好了,DHTML里面addEvent是什么来着?

if (supportsAttachEvent) {
    dhtmlDriver.addEvent = function(e, what, how) {
        e.attachEvent('on' + what, how)
    }
}

然后呢?位于原型链最前端的应该是W3C的标准驱动啊,写上!

var w3cDriver = derive(dhtmlDriverBugfix);
var w3cDriverBugfix = derive(w3cDriver);

if (supportsAddEventListener) {
    w3cDriver.addEvent = function(e, what, how) {
        e.addEventListener(what, how)
    }
}

最后,我们就放个东西上去做最后调用的接口。(因为w3cDriverBugfix太难看……)

var driver = derive(w3cDriverBugfix);

然后就调用好了。看,这就让那些长得吓人的分支判断变得简单有效,但不失fallback本色:在支持addEventListener上调用addEvent等价于调用w3cDriver.addEvent,而在不支持addEventListener的客户端上就会跌落到底下,比如调用dhtmlDriver.addEvent。另外,进行bugfix也很容易——可以在专门的“bugfix”层进行hook,而原有层丝毫不受影响。

等等,继承这么多层

会很慢么?诚然,那么深的原型链肯定会慢,不过我有办法。还记得给对象的属性写入时会发生什么事情吗?

var ego = function(x) {return x}
for (var each in driver) {
    if (! (each in nullDriver)) {
        driver[each] = ego(driver[each])
    }
}

没错,原来高企在原型链上面的方法会“哗”的一下掉到最下面!这回不用沿着原型链向上搜了,直接从最底端获取属性即可。这里用ego函数的原因是防止一些浏览器“优化掉”这里的代码。

总结

虽然这里谈兼容,可是,它的精华却在语言特性上——利用原型继承,我们可以很优雅地完成这个令人头疼的操作。是的,框架的美感不应该只在外表,其内部——即使是最最令人烦的内部——也同样要优雅。

这里的技术可以在dess中找到。

This entry was posted in Browser, 中文 and tagged , , , , . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

15 Comments

  1. hehe123
    Posted August 10, 2010 at 3:33 pm | Permalink

    infinte 精彩的思路,和意想不到的解决方式,让我眼前一亮。 可惜没有抓住灵感。

  2. Posted August 11, 2010 at 2:02 am | Permalink

    很好的文章,我也在网上见过介绍类似技术的BLOG,可惜忘了出处了,这博文我收藏了

  3. Posted August 11, 2010 at 2:19 am | Permalink

    回了一篇博客:http://lifesinger.org/blog/2010/08/graceful-is-hard/
    感谢 infinte 的尝试

  4. tcdona
    Posted August 11, 2010 at 3:30 am | Permalink

    漂亮,真是佩服,对js的奇妙运用

  5. Posted August 11, 2010 at 5:00 am | Permalink

    灰常好的一篇文章…

  6. Posted August 11, 2010 at 6:22 am | Permalink

    好文,收藏到 XXX…

    -,-… 绕了好大的圈。

  7. Posted August 12, 2010 at 3:52 am | Permalink

    http://www.slideshare.net/jaffathecake/optimising-where-it-hurts-jake-archibald

    这里47页开始有个关于深层次作用域链对性能影响的测试.有详细的结论和测试方法.
    在作用域链层次大于5的时候,其在IE8对速度的影响突然变高.

    深层次的原型链可能不一样,但可以应用类似的方法拿准确的数据说话
    如果性能没问题,支持这种设计.

    • Posted August 12, 2010 at 4:11 am | Permalink

      最后有个下压就是干这个的,优化性能。

      • Posted August 12, 2010 at 6:43 am | Permalink

        是逐层复制引用下来?
        在访问属性的时候很好.
        在创建实例时候的性能消耗怎样?

      • Belleve Invis
        Posted August 12, 2010 at 10:21 am | Permalink

        我的天啊,driver是单件哎……不用创建实例。

      • Posted August 13, 2010 at 3:04 am | Permalink

        呵呵 好的 明白了.我以为会按类似的思路开发组件..

  8. akira
    Posted August 12, 2010 at 6:36 am | Permalink

    最后那个下压
    var ego = function(x) {return x}
    for (var each in driver) {
    if (! (driver.hasOwnProperty(each))) {
    driver[each] = ego(driver[each])
    }
    }

  9. Posted September 1, 2010 at 1:11 am | Permalink

    利用JS的原型继承实现更优雅的兼容

  10. Posted October 4, 2010 at 7:25 pm | Permalink

    这个应该就是不唐突和渐进增强,检查能力而不是检查浏览器。

  11. fd
    Posted September 23, 2011 at 2:06 am | Permalink

    晕 换汤不换药……

2 Trackbacks

  1. By 更优雅的兼容 « Coro/infinte on August 10, 2010 at 2:56 pm

    [...] 对于JS框架开发中的客户端(浏览器)兼容难题,各位想必都不陌生。平常,我们都用if去面对接口不一致以及成堆的bug。然而,这里介绍的方法却可以让兼容更加优雅。 原文地址 [...]

  2. By javascript事件机制 « Plane Art on April 3, 2011 at 6:47 pm

    [...] javascript 跨浏览器的事件系统(司徒正美。他博客有一系列的讲解) 更优雅的兼容(BELLEVE [...]

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>