深入理解JavaScript系列(15) 函数(Functions)(5)


var foo = function bar() {
alert('foo');
};
alert(typeof bar); // "function",
// 有趣的是
alert(foo === bar); // false!
foo.x = 10;
alert(bar.x); // 未定义
// 但执行的时候结果一样
foo(); // "foo"
bar(); // "foo"


再次看到,已经乱成一片了。
但是,需要注意的是,如果与变量赋值分开,单独描述NFE(如通过组运算符),然后将它赋给一个变量,并检查其相等性,结果为true,就好像是一个对象。

复制代码 代码如下:


(function bar() {});
var foo = bar;
alert(foo === bar); // true
foo.x = 10;
alert(bar.x); // 10


此时是可以解释的。实际上,再次创建两个对象,但那样做事实上仍保持一个。如果我们再次认为这里的NFE被作为FD对待,然后在进入上下文阶段创建FD bar。此后,在代码执行阶段第二个对象——函数表达式(FE)bar 被创建,它不会被存储。相应地,没有FE bar的任何引用,它被移除了。这样就只有一个对象——FD bar,对它的引用赋给了变量foo。
第三,就通过arguments.callee间接引用一个函数而言,它引用的是被激活的那个对象的名称(确切的说——再这里有两个函数对象。

复制代码 代码如下:


var foo = function bar() {
alert([
arguments.callee === foo,
arguments.callee === bar
]);
};
foo(); // [true, false]
bar(); // [false, true]


第四,JScript 像对待普通的FD一样对待NFE,他不服从条件表达式规则。即,就像一个FD,NFE在进入上下文时创建,在代码中最后的定义被使用。

复制代码 代码如下:


var foo = function bar() {
alert(1);
};
if (false) {
foo = function bar() {
alert(2);
};
}
bar(); // 2
foo(); // 1


这种行为从”逻辑上”也可以解释。在进入上下文阶段,最后遇到的FD bar被创建,即包含alert(2)的函数。此后,在代码执行阶段,新的函数——FE bar创建,对它的引用赋给了变量foo。这样foo激活产生alert(1)。逻辑很清楚,但考虑到IE的bug,既然执行明显被破坏,并依赖于JScript 的bug,我给单词”逻辑上(logically)”加上了引号。
JScript 的第五个bug与全局对象的属性创建相关,全局对象由赋值给一个未限定的标识符(即,没有var关键字)来生成。既然NFE在这被作为FD对待,相应地,它存储在变量对象中,赋给一个未限定的标识符(即不是赋给变量而是全局对象的普通属性),万一函数的名称与未限定的标识符相同,这样该属性就不是全局的了。

复制代码 代码如下:


(function () {
// 不用var的话,就不是当前上下文的一个变量了
// 而是全局对象的一个属性
foo = function foo() {};
})();
// 但,在匿名函数的外部,foo这个名字是不可用的
alert(typeof foo); // 未定义


“逻辑”已经很清楚了:在进入上下文阶段,函数声明foo取得了匿名函数局部上下文的活动对象。在代码执行阶段,名称foo在AO中已经存在,即,它被作为局部变量。相应地,在赋值操作中,只是简单的更新已存在于AO中的属性foo,而不是按照ECMA-262-3的逻辑创建全局对象的新属性。
通过函数构造器创建的函数
既然这种函数对象也有自己的特色,我们将它与FD和FE区分开来。其主要特点在于这种函数的[[Scope]]属性仅包含全局对象:

复制代码 代码如下:


var x = 10;
function foo() {
var x = 20;
var y = 30;
var bar = new Function('alert(x); alert(y);');
bar(); // 10, "y" 未定义
}


我们看到,函数bar的[[Scope]]属性不包含foo上下文的Ao——变量”y”不能访问,变量”x”从全局对象中取得。顺便提醒一句,Function构造器既可使用new 关键字,也可以没有,这样说来,这些变体是等价的。

这些函数的其他特点与 和相关。作为优化建议(但是,实现上可以不使用优化),规范提供了这些机制。如,如果我们有一个100个元素的数组,在函数的一个循环中,执行可能使用Joined Objects 机制。结果是数组中的所有元素仅一个函数对象可以使用。

复制代码 代码如下:


var a = [];

for (var k = 0; k < 100; k++) {
a[k] = function () {}; // 可能使用了joined objects
}


但是通过函数构造器创建的函数不会被连接。

复制代码 代码如下:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wdjzfw.html