关于javascript:未提升定义为函数调用参数的函数名称。 为什么不?

Function names defined as parameters to a function call aren't hoisted. Why not?

请考虑以下代码。

1
2
3
4
<!DOCTYPE html>

  console.log(a);
  function a() {}

请注意,a在定义之前似乎已被访问。控制台输出是:(jsfiddle)

1
function a() {}

函数和变量名称在任何其他代码运行之前定义,因此console.log调用在此处起作用。这称为吊装。

但是,如果函数被定义为函数调用中的参数,则这不起作用。看看这段代码。

1
2
3
4
5
<!DOCTYPE html>

  function a() {}
  a(function b() {});
  console.log(b);

请注意,函数b是在对a的调用中定义的。不在闭包内,而是在通话中。控制台输出是:(jsfiddle)

1
Uncaught ReferenceError: b is not defined

我想知道为什么会这样。这是预期的行为吗?这在Chrome和Firefox中都会发生。

更新:此jsfiddle显示函数表达式中的名称在定义它们的作用域中永远不可用。但是,名称是在函数本身的范围内定义的。这意味着命名函数表达式可以引用名称,但仅限于函数内部。该名称也存储在函数的name参数中。

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>

  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


EcmaScript§13(参见NOTE下面的段落)定义了如何处理函数表达式和函数声明。

函数声明在构建EnvironmentRecord(第10.5节)时被实例化,因此被提升,稍后将对函数表达式进行求值,并且它的可选标识符对于封闭范围永远不可见。

由于这是一个函数表达式,因此外部作用域的名称不可见。它是一样的

1
2
3
4
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而不是抛出错误的原因是提升(虽然实例化已经提升,但初始化不是):

1
2
3
4
5
6
7
8
9
  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() {}

a(function b() {});内,函数是函数表达式而不是函数声明(只有被挂起的函数声明)。您可能会看一下var functionName = function(){} vs function functionName(){}的区别。


在执行调用a(function b() {});之前,function b()不存在。

JS不会以这种方式搜索代码中的函数。此外,function b() {}不是函数声明,您只是将其作为参数传递。访问该函数的唯一方法是在function a()之内:

1
2
3
function a() {console.log(arguments[0])}
a(function b() {});
//function b() {}

但是,这并不意味着您实际上可以在a()内调用b(),因为b()是在a的函数调用范围内定义的,而不是在函数本身中定义的。基本上,名称b是没用的。