JavaScript的几个诡异问题

先看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最有效的良药是:养成良好的编码习惯。比如:

  • 尽量少用全局变量。
  • 不要在函数表达式中使用具名函数。

做到以上两条,本文出现的诡异问题,就都可以避免出现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值