请考虑以下代码。
<!DOCTYPE html>
<script>
console.log(a);
function a() {}
</script>
请注意a
在定义之前似乎已被访问过。控制台输出为:(jsfiddle)
function a() {}
在运行任何其他代码之前定义函数和变量名称,因此console.log调用在此处起作用。这称为hoisting。
但是如果函数被定义为函数调用中的参数,则这不起作用。看看这段代码。
<!DOCTYPE html>
<script>
function a() {}
a(function b() {});
console.log(b);
</script>
请注意,函数b
是在对a
的调用中定义的。不在闭包内,而是在通话中。控制台输出为:(jsfiddle)
Uncaught ReferenceError: b is not defined
我想知道为什么会这样。这是预期的行为吗?这种情况发生在Chrome和Firefox中。
UPDATE:此jsfiddle表示函数表达式中的名称在定义它们的作用域中永远不可用。但是,名称是在函数本身的范围内定义的。这意味着命名函数表达式可以引用名称,但仅限于函数内部。该名称也存储在函数的name
参数中。
<!DOCTYPE html>
<script>
console.log(a); // undefined
var a = function b() {
console.log(b); // function b() { … };
};
a(); // function b() { … };
console.log(a); // function b() { … };
console.log(a.name); // b
console.log(b); // Uncaught ReferenceError: b is not defined
</script>
答案 0 :(得分:3)
在a(function b() {});
内,函数是函数表达式,而不是函数声明(只有被挂起的函数)。您可以查看var functionName = function() {} vs function functionName() {}的差异。
答案 1 :(得分:3)
EcmaScript §13(参见注意下面的段落)定义了函数表达式和函数声明的处理方式。
函数声明在构建EnvironmentRecord(§10.5)时被实例化,因此被提升,稍后将对函数表达式进行求值,并且它的可选标识符永远不会对封闭范围可见。
由于这是一个函数表达式,因此外部作用域的名称不可见。和
一样a = function(){}; // valid as it is a function expression
a = function b(){};// also valid
// b is is not accessable here, same as the anonymous function, but a of course is.
两者都有效,但外部范围不可见b。但是,在函数声明中省略名称是无效的 函数表达式的可选标识符用于函数体内部的参考(例如,用于启用递归调用)。
更新到您的更新:
第一个console.log产生undefined而不是抛出错误的原因是吊装(虽然实例化被提升,但是初始化不是):
console.log(a); // undefined
var a = function b() {};
console.log(a); // function b() {}
// equals (hoisted):
var a;
console.log(a); // undefined
a = function b() {};
console.log(a); // function b() {}
答案 2 :(得分:0)
function b()
在执行调用a(function b() {});
之前不存在。
JS不会以这种方式搜索代码中的函数。此外,function b() {}
不是函数声明,您只是将其作为参数传递。访问该功能的唯一方法是function a()
:
function a() {console.log(arguments[0])}
a(function b() {});
//function b() {}
但是,这并不意味着您实际上可以在b()
内拨打a()
,因为b()
是在a
的函数调用范围内定义的,而不是功能本身。基本上,名称b
是没用的。