【问题标题】:Function names defined as parameters to a function call aren't hoisted. Why not?定义为函数调用参数的函数名称不会被提升。为什么不?
【发布时间】:2013-01-08 15:47:27
【问题描述】:

考虑下面的代码。

<!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 中都会发生。

更新: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>

【问题讨论】:

  • 命名函数表达式不会在封闭范围内创建符号(想象一下var foo = function bar(){};),除非您使用旧的错误版本的 Internet Explorer(更糟糕的是,我们会使用 bar !== foo ...这是一团糟)。

标签: javascript hoisting


【解决方案1】:

a(function b() {}); 中,函数是一个函数表达式,而不是一个函数声明(只有被提升的)。您可以查看var functionName = function() {} vs function functionName() {} 了解其中的区别。

【讨论】:

    【解决方案2】:

    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() {}
    

    【讨论】:

      【解决方案3】:

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

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

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

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

      【讨论】:

      • 这是不正确/具有误导性的,function b() {}) 是一个匿名命名函数。 b 只能在匿名函数的范围内访问。它永远无法在它之外访问。
      • @HoLyVieR:我从来没有这么说。相反:“访问该函数的唯一方法是在 function a(): 内”
      • 你的第一句话就是我所指的。 function b() doesn't exist until the call a(function b() {}); is executed 表示 b 在外部范围内调用后可以访问,这是错误的。
      • @HoLyVieR:是一个匿名命名函数:这是一个矛盾的说法;)它是一个命名函数表达式(相对于一个anonymous (无名)函数表达式)。
      • b这个名字并不是没用的,因为它可以在函数范围内访问,并且可以用于递归。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-29
      • 2015-08-04
      • 1970-01-01
      • 2014-07-25
      相关资源
      最近更新 更多