函数 - JavaScript
差别
虽然有一些细微的差别,但所起的作用都差不多:
函数名和函数的变量存在着差别。函数名不能被改变,但函数的变量却能够被再分配。函数名只能在函数体内使用。倘若在函数体外使用函数名将会导致错误(如果函数之前是通过一个 var 语句声明的则是 undefined)。例如:
jsvar y = function x() {};
alert(x); // throws an error
当函数是通过 Function's toString method被序列化时,函数名同样也会出现。
另一方面,被函数赋值的变量仅仅受限于它的作用域,该作用域确保包含着该函数被声明时的作用域。
正如第四个例子所展示的那样,函数名与被函数赋值的变量是不相同的。彼此之间没有关系。函数声明同时也创建了一个和函数名相同的变量。因此,与函数表达式定义不同,以函数声明定义的函数能够在它们被定义的作用域内通过函数名而被访问到:
使用用 'new Function'定义的函数没有函数名。 然而,在 SpiderMonkey JavaScript 引擎中,其函数的序列化形式表现的好像它拥有一个名叫"anonymous"的名称一样。比如,使用 alert(new Function()) 输出:
jsfunction anonymous() {}
而实际上其函数并没有名称,anonymous 不是一个可以在函数内被访问到的变量。例如,下面的例子将会导致错误:
jsvar foo = new Function("alert(anonymous);");
foo();
和通过函数表达式定义或者通过 Function 构造函数定义的函数不同,函数声明定义的函数可以在它被声明之前使用。举个例子:
jsfoo(); // alerts FOO!
function foo() {
alert("FOO!");
}
函数表达式定义的函数继承了当前的作用域。换言之,函数构成了闭包。另一方面,Function 构造函数定义的函数不继承任何全局作用域以外的作用域 (那些所有函数都继承的)。
通过函数表达式定义的函数和通过函数声明定义的函数只会被解析一次,而 Function 构造函数定义的函数却不同。也就是说,每次构造函数被调用,传递给 Function 构造函数的函数体字符串都要被解析一次。虽然函数表达式每次都创建了一个闭包,但函数体不会被重复解析,因此函数表达式仍然要快于"new Function(...)"。所以 Function 构造函数应尽可能地避免使用。
有一点应该要注意的,在通过解析 Function 构造函数字符串产生的函数里,内嵌的函数表达式和函数声明不会被重复解析。例如:
jsvar foo = new Function(
"var bar = 'FOO!';\nreturn(function() {\n\talert(bar);\n});",
)();
foo(); // 函数体字符串"function() {\n\talert(bar);\n}"的这一部分不会被重复解析。
函数声明非常容易(经常是意外地)转换为函数表达式。当它不再是一个函数声明:
成为表达式的一部分
不再是函数或者脚本自身的“源元素”(source element)。“源元素”是脚本或函数体中的非嵌套语句。
jsvar x = 0; // source element
if (x === 0) {
// source element
x = 10; // 非 source element
function boo() {} // 非 source element
}
function foo() {
// source element
var y = 20; // source element
function bar() {} // source element
while (y === 10) {
// source element
function blah() {} // 非 source element
y++; //非 source element
}
}