关于函数声明表达式的一道题目

来源:互联网 发布:游戏美工就业前景 编辑:程序博客网 时间:2024/04/30 09:55

f = function() { return true; };
g = function() { return false; };
( function() {
    if (g() && [] == ![]) {
        f = function f() { return false; };
        function g() { return true; };
    }
})();

f(); // What's the result?

 

 

 

 

 

 

 

 

答案在最下边:

f = function() { return true; };            // 1
g = function() { return false; };           // 2
(function() {                               // F
                                            // A
    if (g() && [] == ![]) {
        f = function f() { return false; }; // 3
        function g() { return true; };      // 4
    }
})();

f(); // What's the result?

IE 6,7,8 会让函数声明体 3(错误地) 和 4(正确地) 在 A 处生效,于是 F 中,f 和 g 都是局部的了。这样,外界的 f 根本没有被赋值,结果是 true。等价的代码是:

f = function() { return true; };            // 1
g = function() { return false; };           // 2
(function() {                               // F
    var f, g;                               // A
    f = function(){ return false };
    g = function(){ return true  };
    if (g() && [] == ![]) {
        f = function () { return false; };  // 3
        // function g() { return true; };   // 4
    }
})();

f(); // What's the result?

Firefox 则是因为支持条件函数定义(1),在 A 处 3 和 4 都没有生效,导致 if 分支不成立,同样没有给外界 f 赋值,得到 true。精确描述它的行为很难,下面是一个等效的代码:

f = function() { return true; };            // 1
g = function() { return false; };           // 2
(function() {                               // F
    var g1;                                 // A
    g1 = g;
    if (g() && [] == ![]) {
        g1 = function () { return true; };
        f = function () { return false; };  // 3
        // function g() { return true; };   // 4
    }
})();

f(); // What's the result?

Chrome、Safari、IE9 beta 则依照了 ECMA 之规范,只有声明 4 在 A 处生效,等效代码是:

f = function() { return true; };            // 1
g = function() { return false; };           // 2
(function() {                               // F
    var g = function () { return true; };   // A
    if (g() && [] == ![]) {
        f = function () { return false; };  // 3
        // function g() { return true; };   // 4
    }
})();

f(); // What's the result?

结果得到 false。

lifesinger说我分析的不深入,好,我将代码加了钩子后捕获各种浏览器的输出,加钩子的代码是:

var trace = function(m, x){console.log(m + ': ' + x); return x};

f = function() { return true; };          // 1
g = function() { return false; };         // 2
trace('[1f] now f', f);
trace('[1g] now g', g);
(function() {                             // F
    trace('[2f] now f', f);
    trace('[2g] now g', g);
    if (trace('g() && [] == ![]', trace('g()',g()) && trace('[] == ![]', [] == trace('![]', ![])))) {
        trace('[3f] now f', f);
        trace('[3g] now g', g);
        f = function f() { return 0; };   // 3
        function g() { return 1; }        // 4
        trace('[4f] now f', f);
        trace('[4g] now g', g);
    }
    trace('[5f] now f', f);
    trace('[5g] now g', g);
})();
trace('[6f] now f', f);
trace('[6g] now g', g);
f()

chorme 6、Safari 5 的输出结果是:

[1f] now f: function () { return true; }
[1g] now g: function () { return false; }
[2f] now f: function () { return true; }
[2g] now g: function g() { return 1; }
g(): true
![]: false
[] == ![]: true
g() && [] == ![]: true
[3f] now f: function () { return true; }
[3g] now g: function g() { return 1; }
[4f] now f: function f() { return 0; }
[4g] now g: function g() { return 1; }
[5f] now f: function f() { return 0; }
[5g] now g: function g() { return 1; }
[6f] now f: function f() { return 0; }
[6g] now g: function () { return false; }
false

可以注意 [2f] 和 [2g] 的输出,chrome 6 遵守了 ECMA 的规定,忽略 3 处的函数声明,启用了 4 处的声明。所以你可以看到 [2g] 的里面是一个 1。而 f 则是直到 [4f] 才赋值为 function f(){return 0}。

Firefox 3.6.8 的输出结果是:

[1f] now f: function () { return true; }
[1g] now g: function () { return false; }
[2f] now f: function () { return true; }
[2g] now g: function () { return false; }
g(): false
g() && [] == ![]: false
[5f] now f: function () { return true; }
[5g] now g: function () { return false; }
[6f] now f: function () { return true; }
[6g] now g: function () { return false; }
true

可以看出少了好几行,这是因为 if 的短路所致。因为我们把 function g() 给弄进了 if 里,所以 [2g] 处的结果仍然是外界的 g。

IE 6,7,8 的输出是:

日志: [1f] now f: function() { return true; }
日志: [1g] now g: function() { return false; }
日志: [2f] now f: function f() { return 0; }
日志: [2g] now g: function g() { return 1; }
日志: g(): true
日志: ![]: false
日志: [] == ![]: true
日志: g() && [] == ![]: true
日志: [3f] now f: function f() { return 0; }
日志: [3g] now g: function g() { return 1; }
日志: [4f] now f: function f() { return 0; }
日志: [4g] now g: function g() { return 1; }
日志: [5f] now f: function f() { return 0; }
日志: [5g] now g: function g() { return 1; }
日志: [6f] now f: function() { return true; }
日志: [6g] now g: function() { return false; }
true

IE 6,7,8 错误地启用了 3 处的声明,把 F 里定义了一个局部变量 f,所以 [2f] 处得到了不同的结果。因为 f 已经成为 F 的局部变量,因此外界的 f 没有收到影响。

IE9 的输出是:

日志: [1f] now f: function() { return true; }
日志: [1g] now g: function() { return false; }
日志: [2f] now f: function() { return true; }
日志: [2g] now g: function g() { return 1; }
日志: g(): true
日志: ![]: false
日志: [] == ![]: true
日志: g() && [] == ![]: true
日志: [3f] now f: function() { return true; }
日志: [3g] now g: function g() { return 1; }
日志: [4f] now f: function f() { return 0; }
日志: [4g] now g: function g() { return 1; }
日志: [5f] now f: function f() { return 0; }
日志: [5g] now g: function g() { return 1; }
日志: [6f] now f: function f() { return 0; }
日志: [6g] now g: function() { return false; }
false

和 chrome 6 的结果只有空格位置的区别。

 

根据运算符的优先级:先计算[] == ![]  然后在计算g() && 和前边的结果;


至于 [] == ![] 的问题,因为 ![] 等于 false,是个简单值,所以需要把 [] 转换为简单值(toString)。而 [].toString() 的结果是空字符串,所以在 == 的宽松比较下它们相等。

 

 


本人测试结果:

    * IE 6,7,8: true
    * Firefox 3.6.8: true
    * Chrome 6, IE9 beta, Safari 5: false

 

转自:http://lifesinger.org/blog/category/dev/

原创粉丝点击