了解web前端领域的undefined behavior未定义行为

这篇文章发布于 2015年04月18日,星期六,23:13,归类于 Web综合。 阅读 29806 次, 今日 3 次 4 条评论

 

一、本文为超短篇

undefined behavior可以算是计算机世界中特有的一个名字,中文意思是“未定义行为”,维基百科上有解释。在C以及C++语言中,这可以算是一个比较明确的概念:

undefined behavior: use of an unspecified value, or other behavior where this International Standard provides two or more possibilities and imposes no further requirements on which is chosen in any instance

实际上,在web前端领域,也有undefined behavior未定义行为,且广泛程度远远超出了你的想象。

undefined behavior从web标准出现之后,实际上就广泛存在web网页前端技术中,我们很多时候遇到的浏览器兼容性问题,可能就属于undefined behavior。粗读这句话,好像web标准干了什么坏事。实际上并不是,在web标准没出现之前,很多浏览器都有各自的行为表现,我们称之为兼容性问题,但是,这不能称之为undefined behavior未定义行为。为什么呢?因为没有一个标准作为依据,未定义行为之所以称之为未定义行为,就是因其游离于标准之外。我们换种说法可能更好理解,undefined behavior又可以理解为法律空白。你要先有法律,才有法律空白。在法律界,典型的未定义行为就是「同性性侵」。所以伪娘深夜更不能独行,否则被占了便宜还不能法制了色狼,亏大了。

web标准的出现让浏览器间的行为越来越统一,做出了非常重要的贡献。基本上,在现代浏览器中,支持先后这个不算,就一般的CSS或JS,我们通常使用都没有兼容性问题。但是,凡事可能会发生的事情一定会发生。对于IE/Chrome/FireFox三大家族浏览器,一旦发现某个浏览器跟其他两个不一样,我们的反应可能会这样:

  1. 如果是IE使性子,和Chrome和FireFox表现都不一样,我们会认为IE出bug了!
  2. 如果是Chrome耍大牌,跟IE和FireFox表现不一样,我们会替Chrome辩护(就像下面这样的评论,来自这里)
    为Chrome浏览器辩护
  3. 如果是FireFox闹脾气,跟IE和Chrome表现不一样,我们会略同情,FireFox你怎么了,你不再振作怎么跟其他浏览器抗衡啊!

一旦货比三家,发现某一户跟其他所有人家都不一样,其实,我们更多地是认为这个搞特殊的浏览器出bug了!鄙人其实就这么认为了,汗~

但是,自动了解了undefined behavior未定义行为之后,我发现我之前的认知有些狭隘了。“Chrome absolute绝对定位display/visibility渲染bug”一文中提到的问题可能并不是bug,而只是“未定义行为”,也就是web标准并没有详细描述此时的行为表现,而Chrome浏览器根据自己的理解或者规则去处理了(无重绘、更高性能),人家只是在规则允许范围内做了点个性的事情,怎么能就叫人家bug呢!!当然,可能属于未定义行为,只是可能,我还是觉得是Chrome浏览器自己的问题。

但是,下面这个浏览器差异现象,我99%确信是“未定义行为”,一起来看下吧!

二、FireFox浏览器下“未定义行为”一则

我们都知道,CSS中有个伪类:active, IE9+之后的浏览器行为表现非常统一,鼠标按下,执行:active伪类对应的CSS样式,鼠标抬起还原。于是,我们可能很轻松地实现鼠标按下的效果(老IE浏览器失焦效果样式还原)。例如,下面这个红色区域,按下会变色,抬起又恢复:

按我

这个伪类是没有任何问题的,但是,当遭遇其他一些处理的时候,事情就会变得不一样,什么事情呢?

假设此按钮的DOM对象变量名为button, 则我们写一个小脚本,例如:

button.addEventListener("mousedown", function(event) {
    // 此处省略N行
    event.preventDefault();	
});

上面这段脚本,连不是圈子里的我的夫人都知道意思,就是按钮在按下的时候,阻止默认行为(这样拖动的时候就会很流畅)。

看似平淡无奇,但是,最后发生了不和谐的事情:IE和Chrome浏览器,:active伪类依然活蹦乱跳,但是,FireFox浏览器的:active伪类样式阵亡了,鼠标按下去没有UI变化!

百闻不如一见,您可以狠狠地点击这里:FireFox mousedown preventDefault使active伪类失效Demo

demo页面中,上一个按钮就是很普通的CSS按钮,下面一个按钮,CSS部分跟上面一样,但是,实际上,其被JS给玷污了下,绑定了个mousedown事件,同时还阻止了默认行为。但是,IE和Chrome浏览器,上面两个按钮点击都很开心,但是,FireFox浏览器,下面这个按钮,按下效果木有啦!

FireFox浏览器按下没有效果

这里,FireFox和IE, Chrome浏览器表现不一样,这是FireFox浏览器的bug吗?亲,这可不是bug, 而是w3规范上并没有对这种场景的具体描述,所以,FireFox认为active发生在mousedown事件之后,你也不能说他什么,对吧。就好像篮球比赛,我领先,我就用完24秒再出手,怎么着,你来咬我啊!

对吧,你不能说FireFox浏览器的不是,最多我们道德上谴责她,回头呢,我们还得老老实实想办法打个小布丁,例如,判断是FireFox浏览器,然后加个类名active(见下代码红色高亮)(移除类名active脚本略),CSS中再多个.active的样式:

button.addEventListener("mousedown", function(event) {
    // 此处省略N行
    if (typeof document.mozHidden != "undefined") this.classList.add("active");
    event.preventDefault();	
});

三、结语

昨晚看了部电影《地球上的星星》,上帝关上一扇门,也会关掉另外一扇。

地球上的星星

我平时话少,跟话痨相加,正好100%。但是,写东西时候,话却很多。下笔写标题时候,我真觉得几百字就结束了,结果,一写不可收拾,从预想的超短篇变成了短篇出头一点点。

擦,我在干什么,又在瞎扯淡了。速速结语……

结语就是没有结语!

(本篇完)

分享到:


发表评论(目前4 条评论)

  1. zzc说道:

    张老师的文章就是这么通俗易懂而且风趣幽默

  2. sdgds说道:

    还不错

  3. humphry说道:

    被截图放到文章里了,捂脸

  4. vajoy说道:

    才发现居然有document.hidden特性,这个对我一个移动项目挺有帮助,谢谢老师了哈哈