JS 有名函数表达式全面解析有名函数表达式全面解析
Example #1: Function expression identifier leaks into an enclosing scope
实例1:函数表达式标示符渗进了外围作用域
var f = function g(){};
typeof g; // “function”
Remember how I mentioned that an identifier of named function expression is not available in an enclosing scope? Well,
JScript doesn’t agree with specs on this one – g in the above example resolves to a function object. This is a most widely
observed discrepancy. It’s dangerous in that it inadvertedly pollutes an enclosing scope – a scope that might as well be a
global one – with an extra identifier. Such pollution can, of course, be a source of hard-to-track bugs.
我刚才提到过一个有名函数表达式的标示符不能在外部作用域中被访问。但是,JScript在这点上和标准并不相符,在上面的
饿例子中g却是一个函数 对象。这个是一个可以广泛观察到的差异。这样它就用一个多余的标示符污染了外围作用域,这个作
用域很有可能是全局作用域,这样是很危险的。当然这个污染可 能是一个很难去处理和跟踪的bug的根源
Example #2: Named function expression is treated as BOTH – function declaration AND function expression
实例2:有名函数表达式被进行了双重处理,函数表达式和函数声明
typeof g; // “function”
var f = function g(){};
As I explained before, function declarations are parsed foremost any other expressions in a particular execution context. The
above example demonstrates how JScript actually treats named function expressions as function declarations. You can see
that it parses g before an “actual declaration” takes place.
正如我前面解释的,在一个特定的执行环境中,函数声明是在所有的表达式之前被解释。上面的例子说明JScript实际上把有
名函数表达式作为一个函 数声明来对待。我们可以看到他在一个实际的声明之前就被解释了。
This brings us to a next example:
在此基础上我们引入了下面的一个例子。
Example #3: Named function expression creates TWO DISCTINCT function objects!
实例3:有名函数表达式创建两个不同的函数对象。
var f = function g(){};
f === g; // false
f.expando = ‘foo’;
g.expando; // undefined
This is where things are getting interesting. Or rather – completely nuts. Here we are seeing the dangers of having to deal
with two distinct objects – augmenting one of them obviously does not modify the other one; This could be quite troublesome
if you decided to employ, say, caching mechanism and store something in a property of f, then tried accessing it as a
property of g, thinking that it is the same object you’re working with.
在这里事情变得更加有趣了,或者是完全疯掉。这里我们看到必须处理两个不同的对象的危险,当扩充他们当中的一个的时
候,另外一个不会相应的改变。如 果你打算使用cache机制并且在f的属性中存放一些东西,只有有试图在g的属性中访问,你
本以为他们指向同一个对象,这样就会变得非常麻烦
Let’s look at something a bit more complex.
让我们来看一些更复杂的例子。
Example #4: Function declarations are parsed sequentially and are not affected by conditional blocks
实例4:函数声明被顺序的解释,不受条件块的影响
var f = function g() {
return 1;
};
if (false) {
f = function g(){
return 2;
}
};
g(); // 2
An example like this could cause even harder to track bugs. What happens here is actually quite simple. First, g is being
parsed as a function declaration, and since declarations in JScript are independent of conditional blocks, g is being declared
as a function from the “dead” if branch – function g(){ return 2 }. Then all of the “regular” expressions are being evaluated
and f is being assigned another, newly created function object to. “dead” if branch is never entered when evaluating
expressions, so f keeps referencing first function – function g(){ return 1 }. It should be clear by now, that if you’re not careful
enough, and call g from within f, you’ll end up calling a completely unrelated g function object.
像这样的一个例子可能会使跟踪bug非常困难。这里发生的问题却非常简单。首先g被解释为一个函数声明,并且既然JScript
中的声明是和条件块 无关的,g就作为来自于已经无效的if分支中的函数被声明function g(){ return 2 }。之后普通的表达式被求
值并且f被赋值为另外一个新创建的函数对象。当执行表达式的时候,由于if条件分支是不会被进入的,因此f保持为第一函数的
引用 function g(){ return 1 }。现在清楚了如果不是很小心,而且在f内部调用g,你最终将调用一个完全无关的g函数对象。
You might be wondering how all this mess with different function objects compares to arguments.callee. Does callee
reference f or g? Let’s take a look:
你可能在想不从的函数对象和arguments.callee相比较的结果会是怎样呢?callee是引用f还是g?让我们来看一下
var f = function g(){
return [
arguments.callee == f,
arguments.callee == g