6

我正在关注John Resig 的 JS ninja 的秘密幻灯片,我发现了一些我不清楚的东西。以下代码定义了一个命名函数表达式

var ninja = function myNinja(){
  console.log(myNinja); // function myNinja() {...}
};
myNinja; // undefined

正如我所看到的,在当前范围内(假设它是全局的),ninja是保存对命名函数的引用的变量myNinjaninja变量在范围内是可访问的——这很清楚,但myNinja在范围内是不可访问的(但在它自己的函数内是可访问的)。怎么来的?

如果我定义一个函数(不是使用函数表达式,而是函数声明):

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

那么它可以在当前范围内访问。我知道这就是这样 - 但有人可以解释为什么会这样吗?

4

4 回答 4

4

在他的《JavaScript Ninja 的秘密》一书中,John Resig 对这个概念进行了精彩的解释。

http://jsninja.com/

以下是书中的引文:

4.2.4. 内联命名函数

 <script type="text/javascript">
  var ninja = function myNinja(){ 
      assert(ninja == myNinja, "this is named two things at once!"); 
  };
  ninja(); 
  assert(typeof myNinja == "undefined",
    "But myNinja isn't defined outside of the function."); 
 </script>

这个清单提出了关于内联函数的最重要的一点:即使可以命名内联函数,这些名称也只能在函数本身中可见。

还记得我们在第 3 章中讨论过的范围规则吗? 内联函数名有点像变量名,它们的范围仅限于声明它们的函数。

3.2.1。范围和功能

变量声明的范围从它们的声明点到声明它们的函数的末尾,无论块嵌套如何。

如果您想了解更多有关此概念的信息,本书将为您提供帮助。

于 2013-07-03T12:04:09.840 回答
1

这是为什么?

函数表达式每次计算时都会创建一个新的函数对象。这个结果会发生什么一开始是无关紧要的。但是……</p>

var ninja;
// myNinja; - what would you expect it to be here?
for (var i=0; i<5; i++)
    ninja = function myNinja(){
        console.log(myNinja);
    };
// myNinja; - *which one* would you expect it to be here?
ninja();

调用ninja()很明显,它引用了最后分配给该变量的函数。并且myNinjaconsole.log引用当前函数对象 - 它在它自己的范围内。

但是myNinja标识符在函数本身之外是不明确的。

相反,函数声明被提升并可从整个范围访问。它的标识符唯一地指代在作用域初始化中创建一次的单个函数对象。

于 2013-07-03T12:24:38.760 回答
0

名称是函数对象的属性。如果您要在调试器中检查 Function 实例,您将name在其上看到一个属性。对于在全局范围内定义的函数, 的值name会自动用作 window引用它的对象的属性名称。

对于内联函数,您正在定义将引用该函数的属性的名称。JS 引擎不需要使用nameFunction 实例中的属性。

于 2013-07-03T12:48:45.740 回答
0

想象一下,当你简单地定义一个函数(cheese)时,你告诉当前的作用域——“我想让你知道这个函数,它的名字是奶酪。当你使用 var ninja 时,你现在正在处理 ninja 作用域,告诉它基本上是一样的所以现在你的新功能(myNinja)只在当前(忍者)范围内知道......

于 2013-07-03T11:29:31.933 回答