先看IE的一个现象
从非常简单的一个例子开始:
f(); function f() { alert('test'); }
上面的代码所有浏览器都能正确执行,原因是“预解析”(如果不了解,请阅读 JavaScript运行机制浅探)。
稍微变化一下:
f(); var a = function f() { alert('test'); };
上面的代码在IE下能正确执行,在其它浏览器下,提示f未定义。原因是:函数表达式和函数声明是不同的语法结构,非IE浏览器只会预解析函数声明,IE却会连函数表达式也一块预解析了。
Firefox的诡异现象
休息下,看个谁都知道答案的:
var test = 'right'; function f() { alert(test); }; f(); // => 'right'
休息完毕,来点花样:
Object.prototype.test = 'wrong'; var test = 'right'; (function f() { alert(test); })();
Firefox的结果是’wrong’, 其它浏览器都是’right’. 诡异的Firefox, 这究竟是为什么呢?
将代码稍微修改下:
Object.prototype.test = 'wrong'; var test = 'right'; (function f() { alert(test); alert(window.test); alert(f.test); })();
可以看出,在Firefox下,function f() { ... }
中的test等于f.test. 更进一步可以发现,Firefox下,f.test == f.__proto__.test
, 因此值为wrong.
再做一些改变:
Object.prototype.test = 'wrong'; var test = 'right'; (function() { alert(test); })();
变成匿名函数后,所有浏览器都弹出’right’了。
更诡异的是,将第一行去掉:
var test = 'right'; (function f() { alert(test); })();
具名函数,在Firefox下,也能正确输出’right’了。
问题似乎可以简单总结为:Firefox下,Object.prototype.test会影响具名函数内部的test变量。
想弄明白为什么
对于函数声明,我们都很清楚:
function functionName(args) { // body }
除了上面这种语法格式,其它写法中出现的function,都是函数表达式:
(function() { alert('haha'); })(); (function xx() { alert('haha'); })(); void function() { alert('haha'); }(); !function() { alert('haha'); }();
无论是括号()
(强制运算符),还是void
,甚至!
等运算符,都将function变成了语法结构中的函数表达式,在运行期才会解析(IE除外)。
回到Firefox下具名函数表达式变量的解析问题:
Object.prototype.test = 'wrong'; (function () { alert({}.test); alert(window.test); })();
事情越来越诡异,IE下,window.test居然等于undefined.
再看一个越发让人发昏的例子:
alert(window instanceof Object);
Firefox和IE下,为false. Chrome, Safari, Opera为true.
也就是说,Firefox和IE下的window对象并没有继承自Object, 其内部有更复杂的处理机制(期待源码级揭秘),这导致了以上各种诡异问题。
并非所有问题都需要探根究底
总结下上文中提及的所有诡异问题:
1. IE对函数表达式也做预解析的bug:
f(); (function f() { alert('test') })();
2. IE的window.test, 不会追溯到Object.prototype.test:
Object.prototype.test = 'test'; alert(window.test);
3. Firefox的window.test, 在具名函数表达式中,某些写法下,读取时会出错:
Object.prototype.test = 'test'; var test = 'test2'; (function f() { alert(test); })();
最后两个问题的原因是:Firefox和IE下的window对象没有直接继承自Object, 其内部有更复杂的处理机制。
和很多CSS bugs一样,以上问题并不一定都值得我们花费大量时间去探根究底。我们只要记住现象,知道如何避免,就足够了。对《Explorer 6 Duplicate Characters Bug》一文的最后一句话记忆犹新:
The effects can get even weirder but there’s no need to become obsessed, so we’ll quietly close the door on this crazy bug.
良好的编码习惯
避免以上各种诡异bugs最有效的良药是:养成良好的编码习惯。比如:
- 尽量少用全局变量。
- 不要在函数表达式中使用具名函数。
做到以上两条,本文出现的诡异问题,就都可以避免出现。