什么是词法范围?

IT技术 javascript scoping lexical-scope
2021-01-19 06:43:32

什么是词法范围的简要介绍?

6个回答

我通过例子来理解它们。:)

首先,词法作用域(也称为静态作用域),在类似 C 的语法中:

void fun()
{
    int x = 5;

    void fun2()
    {
        printf("%d", x);
    }
}

每个内层都可以访问其外层。

还有另一种方式,称为Lisp的第一个实现使用的动态作用域,同样采用类似 C 的语法:

void fun()
{
    printf("%d", x);
}

void dummy1()
{
    int x = 5;

    fun();
}

void dummy2()
{
    int x = 10;

    fun();
}

Herefun可以访问xindummy1dummy2,或任何x调用fun其中x声明的函数中的任何函数

dummy1();

将打印 5,

dummy2();

将打印 10。

第一个被称为静态,因为它可以在编译时推导出来,第二个被称为动态,因为外部作用域是动态的,并且依赖于函数的链调用。

我发现静态范围对眼睛来说更容易。大多数语言最终都采用了这种方式,甚至 Lisp(两者都可以,对吧?)。动态范围就像将所有变量的引用传递给被调用函数。

作为编译器为什么不能推导出函数的外部动态范围的示例,请考虑我们的最后一个示例。如果我们这样写:

if(/* some condition */)
    dummy1();
else
    dummy2();

调用链取决于运行时条件。如果为真,则调用链如下所示:

dummy1 --> fun()

如果条件为假:

dummy2 --> fun()

fun两种情况下的外部作用域都是调用者加上调用者的调用者等等

只是提到 C 语言不允许嵌套函数或动态范围。

非常好的答案。谢谢你。@Boyang 我不同意。我不是 Lisp 编码员,但发现 Lisp 示例很有帮助,因为它是动态范围的示例,而您在 JS 中没有。
2021-03-10 06:43:32
最初我认为这个例子是有效的 C 代码,并且对 C 中是否有动态范围感到困惑。也许最后的免责声明可以移到代码示例之前?
2021-03-23 06:43:32
这是一个很好的答案。但问题被标记为JavaScript因此,我认为这不应被标记为已接受的答案。专门在 JS 中的词法范围是不同的
2021-03-27 06:43:32
这仍然是一个非常有用的答案,但我认为@Boyang 是正确的。这个答案指的是“级别”,它更符合 C 的块范围。JavaScript 默认没有块级作用域,所以在for循环内部是典型的问题。JavaScript 的词法范围仅在函数级别,除非使用 ES6letconst
2021-04-05 06:43:32
我还想指出我刚刚找到的一个非常容易理解的教程。Arak 的例子很好,但对于需要更多例子的人来说可能太短了(实际上,与其他语言相比......)。看一看。理解this很重要,因为该关键字将引导我们理解词法范围。howtonode.org/what-is-this
2021-04-06 06:43:32

让我们尝试最短的定义:

词法作用域定义了如何在嵌套函数中解析变量名:即使父函数已经返回,内部函数也包含父函数的作用域

这就是全部!

最后一部分:“即使父函数已经返回”称为闭包。
2021-03-19 06:43:32
仅用一句话就理解了词法范围和闭包。谢谢!!
2021-04-09 06:43:32
var scope = "I am global";
function whatismyscope(){
   var scope = "I am just a local";
   function func() {return scope;}
   return func;
}

whatismyscope()()

上面的代码将返回“我只是一个本地人”。它不会返回“我是全球性的”。因为函数 func() 计算了最初定义的地方是在函数 whatismyscope 的范围内。

无论它被调用什么(全局范围/甚至来自另一个函数),它都不会打扰,这就是为什么我是全局的全局范围值不会被打印。

这称为词法作用域,其中“函数使用定义时有效的作用域链执行” - 根据 JavaScript 定义指南。

词法范围是一个非常强大的概念。

希望这可以帮助..:)

很好的解释,如果你写函数 func() {return this.scope;} 然后它会返回“我是全局的”,我想再添加一件事,只要使用这个关键字,你的范围就会改变
2021-04-07 06:43:32

词法(又名静态)范围是指仅根据变量在代码文本语料库中的位置来确定变量的范围。变量总是指其顶级环境。最好根据动态范围来理解它

范围定义了可以使用函数、变量等的区域。例如,变量的可用性是在其上下文中定义的,比如说函数、文件或对象,它们是在其中定义的。我们通常称这些为局部变量。

词法部分意味着您可以从阅读源代码中得出范围。

词法作用域也称为静态作用域。

动态范围定义了全局变量,定义后可以从任何地方调用或引用。有时它们被称为全局变量,尽管大多数程序语言中的全局变量是词法范围的。这意味着,可以通过阅读代码得出变量在此上下文中可用。也许必须遵循 uses 或 includes 子句才能找到实例或定义,但代码/编译器知道这个地方的变量。

相比之下,在动态作用域中,您首先在本地函数中搜索,然后在调用本地函数的函数中搜索,然后在调用该函数的函数中搜索,依此类推,直到调用堆栈。“动态”指的是变化,因为每次调用给定函数时调用堆栈都可能不同,因此该函数可能会根据调用位置而命中不同的变量。(见这里

要查看动态范围的有趣示例,请参见此处

有关更多详细信息,请参阅此处此处

Delphi/Object Pascal 中的一些例子

Delphi 有词法作用域。

unit Main;
uses aUnit;  // makes available all variables in interface section of aUnit

interface

  var aGlobal: string; // global in the scope of all units that use Main;
  type 
    TmyClass = class
      strict private aPrivateVar: Integer; // only known by objects of this class type
                                    // lexical: within class definition, 
                                    // reserved word private   
      public aPublicVar: double;    // known to everyboday that has access to a 
                                    // object of this class type
    end;

implementation

  var aLocalGlobal: string; // known to all functions following 
                            // the definition in this unit    

end.

与动态作用域最接近的 Delphi 是 RegisterClass()/GetClass() 函数对。有关其使用,请参见此处

假设调用 RegisterClass([TmyClass]) 注册某个类的时间无法通过阅读代码来预测(它在用户调用的按钮单击方法中调用),调用 GetClass('TmyClass') 的代码将得到结果与否。对 RegisterClass() 的调用不必在使用 GetClass() 的单元的词法范围内;

动态作用域的另一种可能性是Delphi 2009中的匿名方法(闭包),因为它们知道调用函数的变量。它不会从那里递归地遵循调用路径,因此不是完全动态的。

+1 表示通俗语言(相对于没有太多描述的复杂语言和示例)
2021-03-22 06:43:32
实际上,private 可以在定义类的整个单元中访问。这就是为什么在 D2006 中引入了“严格私有”的原因。
2021-04-05 06:43:32