javascript命名函数表达式-作用域可访问性

javascript named function expressions - scope accessibility

本文关键字:作用域 访问 表达式 函数 javascript      更新时间:2023-09-26

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

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

正如我所看到的,在当前作用域(假设它是全局的)中,ninja是引用命名函数myNinja的变量。ninja变量在作用域中是可访问的——这很清楚,但myNinja在作用域内是不可访问的(但它可以在自己的函数内访问)。为什么?

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

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

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

在他的书《JavaScript忍者的秘密》中,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.范围界定和功能

变量声明的范围从声明点到声明它们的函数的末尾,而不管块嵌套。

如果你想了解更多关于这个概念的信息,这本书将对你有所帮助。

为什么?

函数表达式每次求值时都会创建一个新的函数对象。这个结果一开始是无关紧要的。但是…

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()的调用是显而易见的,它引用了最后分配给该变量的函数。console.log中的myNinja引用当前函数对象-它在自己的范围内。

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

相反,函数声明是挂起的,并且可以从整个范围访问。其标识符唯一地引用在范围初始化中创建一次的单个函数对象。

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

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

在内联函数的情况下,您将定义引用该函数的属性的名称。JS引擎不需要使用Function实例中的name属性。