什么是词法范围的简要介绍?
什么是词法范围?
我通过例子来理解它们。:)
首先,词法作用域(也称为静态作用域),在类似 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
可以访问x
indummy1
或dummy2
,或任何x
调用fun
其中x
声明的函数中的任何函数。
dummy1();
将打印 5,
dummy2();
将打印 10。
第一个被称为静态,因为它可以在编译时推导出来,第二个被称为动态,因为外部作用域是动态的,并且依赖于函数的链调用。
我发现静态范围对眼睛来说更容易。大多数语言最终都采用了这种方式,甚至 Lisp(两者都可以,对吧?)。动态范围就像将所有变量的引用传递给被调用函数。
作为编译器为什么不能推导出函数的外部动态范围的示例,请考虑我们的最后一个示例。如果我们这样写:
if(/* some condition */)
dummy1();
else
dummy2();
调用链取决于运行时条件。如果为真,则调用链如下所示:
dummy1 --> fun()
如果条件为假:
dummy2 --> fun()
fun
两种情况下的外部作用域都是调用者加上调用者的调用者等等。
只是提到 C 语言不允许嵌套函数或动态范围。
让我们尝试最短的定义:
词法作用域定义了如何在嵌套函数中解析变量名:即使父函数已经返回,内部函数也包含父函数的作用域。
这就是全部!
var scope = "I am global";
function whatismyscope(){
var scope = "I am just a local";
function func() {return scope;}
return func;
}
whatismyscope()()
上面的代码将返回“我只是一个本地人”。它不会返回“我是全球性的”。因为函数 func() 计算了最初定义的地方是在函数 whatismyscope 的范围内。
无论它被调用什么(全局范围/甚至来自另一个函数),它都不会打扰,这就是为什么我是全局的全局范围值不会被打印。
这称为词法作用域,其中“函数使用定义时有效的作用域链执行” - 根据 JavaScript 定义指南。
词法范围是一个非常强大的概念。
希望这可以帮助..:)
范围定义了可以使用函数、变量等的区域。例如,变量的可用性是在其上下文中定义的,比如说函数、文件或对象,它们是在其中定义的。我们通常称这些为局部变量。
词法部分意味着您可以从阅读源代码中得出范围。
词法作用域也称为静态作用域。
动态范围定义了全局变量,定义后可以从任何地方调用或引用。有时它们被称为全局变量,尽管大多数程序语言中的全局变量是词法范围的。这意味着,可以通过阅读代码得出变量在此上下文中可用。也许必须遵循 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中的匿名方法(闭包),因为它们知道调用函数的变量。它不会从那里递归地遵循调用路径,因此不是完全动态的。