定义为函数调用参数的函数名称不会被提升。为什么不?

时间:2013-01-08 15:47:28

标签: javascript hoisting

请考虑以下代码。

<!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>

3 个答案:

答案 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是没用的。