javascript命名函数表达式 - 范围可访问性

IT技术 javascript function
2021-02-26 14:22:47

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

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

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

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

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

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

4个回答

在他的著作The Secrets of the 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. 范围和功能

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

如果你想更多地了解这个概念,这本书会对你有所帮助。

这是为什么?

函数表达式每次求值时都会创建一个新的函数对象。这个结果会发生什么起初无关紧要。但…

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标识符在函数本身之外是不明确的。

相比之下,函数声明被提升并可从整个范围访问。它的标识符唯一地指代在范围初始化时创建的单个函数对象。

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

我试图理解你的解释。我知道当控制进入 javascript 中的一个新函数时,会创建新的作用域上下文并附加新的变量对象 - 这很清楚。现在你写道:当你使用 var ninja 时,你现在正在处理 ninja 范围- 你的意思是当我声明一个新变量(在这种情况下变量指的是一个函数)时,变量有一个新的范围(不是函数)?
2021-05-07 14:22:47

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

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