有人可以给我简要介绍词汇作用域吗?
#1楼
词法(AKA静态)作用域是指仅根据变量在代码文本语料库中的位置来确定变量的范围。 变量始终引用其顶层环境。 最好将其与动态范围相关联。
#2楼
范围定义了功能,变量等可用的区域。 例如,变量的可用性是在其上下文中定义的,也就是说,它们是在函数,文件或对象中定义的。我们通常将这些变量称为局部变量。
词法部分意味着您可以通过阅读源代码来得出范围。
词法范围也称为静态范围。
动态范围定义了可以在定义后从任何地方调用或引用的全局变量。 有时它们被称为全局变量,即使大多数programmin语言中的全局变量具有词法范围。 这意味着,可以从读取代码中得出该变量在此上下文中可用的信息。 也许必须遵循use或include子句才能找到实例或定义,但是代码/编译器知道该位置的变量。
相比之下,在动态作用域中,您首先搜索本地函数,然后搜索调用本地函数的函数,然后搜索调用该函数的函数,依此类推,直到调用堆栈。 “动态”是指更改,因为每次调用给定函数时,调用堆栈都可能不同,因此该函数可能会根据调用源的不同而使用不同的变量。 (请参阅此处 )
要查看动态范围的有趣示例,请参见此处 。
有关更多详细信息,请参见此处和此处 。
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')的代码将获得结果与否。 使用GetClass()调用RegisterClass()不必在单元的词汇范围内;
动态范围的另一种可能性是Delphi 2009中的匿名方法 (闭包),因为它们知道其调用函数的变量。 它不会从那里递归地遵循调用路径,因此不是完全动态的。
#3楼
我通过示例了解它们。 :)
首先,采用类似C的语法的词法作用域(也称为静态作用域):
void fun()
{
int x = 5;
void fun2()
{
printf("%d", x);
}
}
每个内部级别都可以访问其外部级别。
同样,在类似C的语法中,Lisp的第一个实现使用了另一种称为动态范围的方法:
void fun()
{
printf("%d", x);
}
void dummy1()
{
int x = 5;
fun();
}
void dummy2()
{
int x = 10;
fun();
}
这里fun既可以访问x在dummy1或dummy2 ,或x任何函数调用fun与x在其声明。
dummy1();
将打印5
dummy2();
将打印10。
第一个称为静态,因为它可以在编译时推导,第二个称为动态,因为外部范围是动态的,并且取决于函数的链调用。
我发现静态范围界定对眼睛来说更容易。 最终,大多数语言甚至都使用Lisp(可以同时做,对吗?)。 动态作用域就像将所有变量的引用传递给调用的函数一样。
为什么编译器无法推断函数的外部动态范围的示例,请考虑我们的最后一个示例,如果我们编写如下代码:
if(/* some condition */)
dummy1();
else
dummy2();
调用链取决于运行时条件。 如果为true,则调用链如下所示:
dummy1 --> fun()
如果条件为假:
dummy2 --> fun()
在这两种情况下, fun的外部范围是调用者加上调用者的调用者,依此类推 。
仅提及C语言既不允许嵌套函数也不允许动态作用域。
#4楼
var scope = "I am global";
function whatismyscope(){
var scope = "I am just a local";
function func() {return scope;}
return func;
}
whatismyscope()()
上面的代码将返回“我只是本地人”。 它不会返回“我是全球”。 因为函数func()会在函数whatismyscope的范围内计算最初定义的位置。
它不会被调用的内容所困扰(即使是全局作用域,甚至也不会从另一个函数中),这就是为什么不会打印我是全局的全局作用域值的原因。
这称为词法作用域,其中“根据JavaScript定义指南,使用定义时有效的作用域链执行函数 ”。
词法范围是一个非常非常强大的概念。
希望这可以帮助..:)
#5楼
我喜欢@Arak之类的功能全面,与语言无关的答案。 由于此问题被标记为JavaScript ,因此,我想介绍一些针对该语言的注释。
在javascript中,作用域的选择是:
原样(不调整范围)
词法var _this = this; function callback(){ console.log(_this); } var _this = this; function callback(){ console.log(_this); }
绑定callback.bind(this)
我认为,值得注意的是JavaScript 并没有真正的动态作用域 。 .bind调整this关键字,这很接近,但在技术上并不相同。
这是演示两种方法的示例。 每次决定如何确定回调范围时,您都需要执行此操作,因此这适用于Promise,事件处理程序等。
您可能会在这里用JavaScript称呼回调的Lexical Scoping :
var downloadManager = {
initialize: function() {
var _this = this; // Set up `_this` for lexical access
$('.downloadLink').on('click', function () {
_this.startDownload();
});
},
startDownload: function(){
this.thinking = true;
// request the file from the server and bind more callbacks for when it returns success or failure
}
//...
};
界
var downloadManager = {
initialize: function() {
$('.downloadLink').on('click', function () {
this.startDownload();
}.bind(this)); // create a function object bound to `this`
}
//...
据我所知,这些方法在行为上是等效的。