Javascript IE第2部分中的命名函数表达式

Javascript IE第2部分中的命名函数表达式,javascript,internet-explorer,anonymous-function,Javascript,Internet Explorer,Anonymous Function,我问了一会儿,很高兴得到了公认的答案。然而,我刚刚意识到以下技术: var testaroo = 0; (function executeOnLoad() { if (testaroo++ < 5) { setTimeout(executeOnLoad, 25); return; } alert(testaroo); // alerts "6" })(); var testaroo=0; (函数executeOnLoad(){ if

我问了一会儿,很高兴得到了公认的答案。然而,我刚刚意识到以下技术:

var testaroo = 0;
(function executeOnLoad() {
    if (testaroo++ < 5) {
        setTimeout(executeOnLoad, 25);
        return;
    }
    alert(testaroo); // alerts "6"
})();
var testaroo=0;
(函数executeOnLoad(){
if(testaroo++<5){
setTimeout(executeOnLoad,25);
返回;
}
警报(testaroo);//警报“6”
})();

返回我期望的结果。如果我的第一个问题的答案是正确的,那么这种技术不应该不起作用吗?

好吧,它会起作用,JScript(IE)的问题是函数表达式的标识符(
executeOnLoad
)将泄漏到它的封闭范围,并实际创建两个函数对象

(function () {
  var myFunc = function foo () {};
  alert(typeof foo); // "undefined" on all browsers, "function" on IE

  if (typeof foo !== "undefined") { // avoid TypeError on other browsers
    alert( foo === myFunc ); // false!, IE actually creates two function objects
  }
})();
一个很好的问题。:-)

区别在于: 这与您的
detachEvent
情况的区别在于,在这里,您并不关心“函数”内外的函数引用是否相同,只关心代码是否相同。在
detachEvent
情况下,您在“函数”内外看到相同的函数引用很重要,因为这就是
detachEvent
的工作方式,通过分离您赋予它的特定函数

两个功能? 对。CMS指出,当IE(JScript)在代码中看到命名函数表达式时,它会创建两个函数。(我们将回到这里。)有趣的是,你给他们两个都打电话。是的,真的。:-)初始调用调用表达式返回的函数,然后使用该名称的所有调用调用另一个函数

稍微修改代码可以使这一点更加清晰:

var testaroo = 0;
var f = function executeOnLoad() {
    if (testaroo++ < 5) {
        setTimeout(executeOnLoad, 25);
        return;
    }
    alert(testaroo); // alerts "6"
};
f();
这不仅仅是一些名称的工件,JScript还创建了两个函数

发生什么事? 基本上,实现JScript的人显然决定将命名函数表达式作为函数声明和函数表达式进行处理,在处理过程中创建两个函数对象(一个来自“声明”,一个来自“表达式”),并且几乎可以肯定在不同的时间这样做。这是完全错误的,但这是他们所做的。令人惊讶的是,即使是IE8中的新JScript也继续这种行为

这就是代码看到(并使用)两个不同函数的原因。这也是CMS提到“泄漏”这个名字的原因。转换到他的示例的稍加修改的副本:

function outer() {
    var myFunc = function inner() {};

    alert(typeof inner); // "undefined" on most browsers, "function" on IE

    if (typeof inner !== "undefined") { // avoid TypeError on other browsers
        // IE actually creates two function objects: Two proofs:
        alert(inner === myFunc); // false!
        inner.foo = "foo";
        alert(inner.foo);        // "foo"
        alert(myFunc.foo);       // undefined
    }
}
正如他所提到的,
internal
是在IE(JScript)上定义的,而不是在其他浏览器上定义的。为什么不呢?对于不经意的观察者来说,除了两个函数之外,JScript关于函数名的行为似乎是正确的。毕竟,只有函数在Javascript中引入了新的作用域,对吗?而
内部
功能在
外部
中有明确定义。但该规范实际上煞费苦心地说不,这个符号在
外部
中没有定义(甚至深入研究了实现如何在不违反其他规则的情况下避免它的细节)。第13节(第3版和第5版)对此进行了介绍。以下是相关的高层报价:

FunctionExpression中的标识符可以从FunctionExpression的FunctionBody内部引用,以允许函数递归调用自身。但是,与FunctionDeclaration不同,FunctionExpression中的标识符不能从中引用,也不会影响包含FunctionExpression的范围

他们为什么要这么麻烦?我不知道,但我怀疑这与函数声明在执行任何语句代码(分步代码)之前进行求值有关,而函数表达式(与所有表达式一样)在控制流中到达时作为语句代码的一部分进行求值。考虑:

function foo() {

    bar();

    function bar() {
        alert("Hi!");
    }
}
当控制流进入函数
foo
时,首先发生的事情之一是
bar
函数被实例化并绑定到符号
bar
;只有这样,解释器才开始处理
foo
函数体中的语句。这就是为什么在顶部调用
bar

但在这里:

function foo() {

    var f;

    f = function() {
        alert("Hi!");
    };

    f();
}
函数表达式在到达时进行求值(很可能,我们不能确定某些实现之前没有这样做)。表达式没有(或不应该)在前面计算的一个很好的原因是:

function foo() {

    var f;

    if (some_condition) {
        f = function() {
            alert("Hi (1)!");
        };
    }
    else {
        f = function() {
            alert("Hi! (2)");
        };
    }

    f();
}
…过早这样做会导致模棱两可和/或浪费精力。这就引出了这里应该发生什么的问题:

function foo() {

    var f;

    bar();

    if (some_condition) {
        f = function bar() {
            alert("Hi (1)!");
        };
    }
    else {
        f = function bar() {
            alert("Hi! (2)");
        };
    }

    f();
}
function outer() {

    bar();

    function bar() {
        alert("Hi (1)!");
    }

    function bar() {
        alert("Hi (2)!");
    }
}
开始时调用哪个
?规范作者选择的解决这种情况的方法是说
bar
根本没有在
foo
中定义,因此完全回避了这个问题。(这不是他们解决问题的唯一方式,但似乎是他们选择的方式。)

那么IE(JScript)是如何处理的呢?开始时调用的
发出“Hi(2)!”警报。这一点,再加上我们知道有两个函数对象是基于我们的其他测试创建的,最清楚地表明JScript将函数表达式命名为函数声明函数表达式,因为这正是这里应该发生的事情:

function foo() {

    var f;

    bar();

    if (some_condition) {
        f = function bar() {
            alert("Hi (1)!");
        };
    }
    else {
        f = function bar() {
            alert("Hi! (2)");
        };
    }

    f();
}
function outer() {

    bar();

    function bar() {
        alert("Hi (1)!");
    }

    function bar() {
        alert("Hi (2)!");
    }
}
这里有两个同名的函数声明。语法错误?你会这么想,但事实并非如此。规范明确允许它,并表示源代码顺序中的第二个声明“获胜”。第三版规范第10.1.3节:

对于代码中的每个FunctionDeclaration,按源代码文本顺序,创建变量对象的属性,其名称为FunctionDeclaration中的标识符…如果变量对象已经具有具有此名称的属性,请替换其值和属性

(变量对象是符号如何被解析的;这完全是另一个话题。)在第五版(第10.5节)中,这一点同样明确,但是,嗯,引用性要小得多

那么,就这么简单了? 只是