词汇范围的简要介绍是什么?
当前回答
在这个问题上,我们可以从另一个角度出发,后退一步,看看范围界定在更大的解释框架(运行程序)中的作用。换句话说,假设您正在为一种语言构建一个解释器(或编译器),并负责计算输出,给定一个程序和一些输入。
解释包括跟踪三件事:
状态-即堆和堆栈上的变量和引用内存位置。对该状态的操作,即程序中的每一行代码给定操作运行的环境,即状态在操作上的投影。
解释器从程序中的第一行代码开始,计算其环境,在该环境中运行该行,并捕获其对程序状态的影响。然后,它遵循程序的控制流执行下一行代码,并重复该过程直到程序结束。
为任何操作计算环境的方式都是通过编程语言定义的一组正式规则。术语“绑定”经常用于描述程序的整体状态到环境中的值的映射。注意,我们所说的“总体状态”不是指全局状态,而是指在执行过程中的任何一点上每个可到达定义的总和)。
这是定义范围问题的框架。现在进入下一部分我们的选择。
作为解释器的实现者,您可以通过使环境尽可能接近程序的状态来简化任务。因此,无论前一行是赋值、函数调用、从函数返回还是控制结构(如while循环),一行代码的环境都将简单地由前一行代码环境定义,并将该操作的效果应用于该行代码。
这就是动态作用域的要点,其中任何代码运行的环境都绑定到由其执行上下文定义的程序状态。
或者,你可以想象一个程序员使用你的语言,简化他或她的跟踪变量取值的任务。关于过去执行的全部结果的推理涉及太多的路径和太多的复杂性。词法作用域通过将当前环境限制在当前块、函数或其他作用域单元及其父级(即包围当前时钟的块或调用当前函数的函数)中定义的状态部分来帮助实现这一点。
换句话说,使用词法作用域,任何代码所看到的环境都绑定到与语言中明确定义的作用域(如块或函数)相关联的状态。
其他回答
词汇范围是指从执行堆栈中的当前位置可见的标识符(例如,变量、函数等)的词汇。
- global execution context
- foo
- bar
- function1 execution context
- foo2
- bar2
- function2 execution context
- foo3
- bar3
foo和bar总是在可用标识符的词典中,因为它们是全局的。
当执行function1时,它可以访问foo2、bar2、foo和bar的词典。
当执行function2时,它可以访问foo3、bar3、foo2、bar2、foo和bar的词典。
全局和/或外部函数无法访问内部函数标识符的原因是,该函数的执行尚未发生,因此,其标识符均未分配给内存。此外,一旦内部上下文执行,它就会从执行堆栈中删除,这意味着它的所有标识符都已被垃圾收集,不再可用。
最后,这就是为什么嵌套的执行上下文总是可以访问它的祖先执行上下文,因此它可以访问更大的标识符词典。
See:
https://tylermcginnis.com/ultimate-guide-to-execution-contexts-hoisting-scopes-and-closures-in-javascript/https://developer.mozilla.org/en-US/docs/Glossary/Identifier
特别感谢@robr3rd帮助简化上述定义。
我喜欢@Arak这样的人提供的功能齐全、语言不可知的答案。由于这个问题被标记为JavaScript,所以我想插入一些与该语言非常相关的注释。
在JavaScript中,我们的作用域选择如下:
按原样(无范围调整)词法var_this=this;函数callback(){console.log(_this);}绑定回调.bind(this)
我认为值得注意的是,JavaScript并没有真正的动态范围。bind调整this关键字,这很接近,但在技术上并不相同。
下面是一个示例,演示了这两种方法。每次决定如何确定回调的范围时,都要这样做,因此这适用于承诺、事件处理程序等。
词汇
以下是JavaScript中回调的词法范围:
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
}
//...
};
跳跃
作用域的另一种方法是使用Function.prototype.bind:
var downloadManager = {
initialize: function() {
$('.downloadLink').on('click', function () {
this.startDownload();
}.bind(this)); // Create a function object bound to `this`
}
//...
据我所知,这些方法在行为上是等效的。
让我们尝试最短的定义:
词法作用域定义了如何在嵌套函数中解析变量名:即使父函数已返回,内部函数也包含父函数的作用域。
这就是它的全部!
JavaScript中的词法作用域意味着在函数外部定义的变量可以在变量声明之后定义的另一个函数内部访问。但事实并非相反;在函数内部定义的变量在该函数外部无法访问。
这个概念在JavaScript中的闭包中大量使用。
假设我们有以下代码。
var x = 2;
var add = function() {
var y = 1;
return x + y;
};
现在,当您调用add()-->时,这将打印3。
因此,add()函数正在访问在方法函数add之前定义的全局变量x。这是由于JavaScript中的词法作用域而调用的。
词法范围:在函数外部声明的变量是全局变量,在JavaScript程序中随处可见。在函数内声明的变量具有函数范围,并且仅对该函数内出现的代码可见。