词汇范围的简要介绍是什么?
当前回答
词法范围:在函数外部声明的变量是全局变量,在JavaScript程序中随处可见。在函数内声明的变量具有函数范围,并且仅对该函数内出现的代码可见。
其他回答
词汇范围意味着函数在其定义的上下文中查找变量,而不是在其周围的范围中查找变量。
如果您想了解更多详细信息,请查看Lisp中词法作用域的工作原理。Kyle Cronin在Common Lisp的动态和词汇变量中选择的答案比这里的答案更清楚。
巧合的是,我只是在Lisp类中了解到这一点,而且它恰好也适用于JavaScript。
我在Chrome控制台中运行了这段代码。
// JavaScript Equivalent Lisp
var x = 5; //(setf x 5)
console.debug(x); //(print x)
function print_x(){ //(defun print-x ()
console.debug(x); // (print x)
} //)
(function(){ //(let
var x = 10; // ((x 10))
console.debug(x); // (print x)
print_x(); // (print-x)
})(); //)
输出:
5
10
5
本主题与内置绑定函数密切相关,并在ECMAScript 6 Arrow函数中介绍。这真的很烦人,因为对于我们想要使用的每一个新的“类”(实际上是函数)方法,我们都必须绑定它才能访问作用域。
默认情况下,JavaScript不会在函数上设置this的范围(它不会设置this的上下文)。默认情况下,您必须明确说出您想要的上下文。
箭头函数自动获得所谓的词法范围(可以访问包含块中变量的定义)。当使用箭头函数时,它会自动将其绑定到最初定义箭头函数的位置,并且该箭头函数的上下文是其包含块。
通过以下最简单的示例,了解它在实践中的工作原理。
在箭头函数之前(默认情况下没有词法范围):
const programming = {
language: "JavaScript",
getLanguage: function() {
return this.language;
}
}
const globalScope = programming.getLanguage;
console.log(globalScope()); // Output: undefined
const localScope = programming.getLanguage.bind(programming);
console.log(localScope()); // Output: "JavaScript"
使用箭头函数(默认为词法范围):
const programming = {
language: "JavaScript",
getLanguage: function() {
return this.language;
}
}
const arrowFunction = () => {
console.log(programming.getLanguage());
}
arrowFunction(); // Output: "JavaScript"
这是一个古老的问题,但这是我对它的看法。
词法(静态)范围是指源代码中变量的范围。
在JavaScript这样的语言中,函数可以被传递、附加和重新附加到其他对象,您可能会想到,这个范围将取决于当时调用该函数的人,但事实并非如此。以这种方式更改作用域将是动态作用域,而JavaScript不会这样做,除非可能使用此对象引用。
要说明这一点:
var a=“苹果”;函数doit(){var a=“ardvark”;返回函数(){警报(a);}}var test=doit();测试();
在示例中,变量a是全局定义的,但在doit()函数中隐藏。此函数返回另一个函数,如您所见,该函数依赖于自身范围之外的变量。
如果您运行这个,您会发现使用的值是aardwark,而不是apple,虽然它在test()函数的范围内,但不在原始函数的词法范围内。也就是说,使用的范围是源代码中显示的范围,而不是实际使用函数的范围。
这一事实可能会产生令人讨厌的后果。例如,您可能会决定更容易单独组织函数,然后在时间到来时使用它们,例如在事件处理程序中:
var a=“苹果”,b=“香蕉”;函数init(){var a=“ardvark”,b=“andicoot”;document.querySelector('button#a').onclick=函数(事件){警报(a);}document.querySelector('button#b').onclick=doB;}函数doB(事件){警报(b);}init();<button id=“a”>a</button><button id=“b”>b</button>
此代码示例分别执行其中一项。您可以看到,由于词法作用域,按钮A使用内部变量,而按钮B不使用。您可能最终嵌套的函数比您想要的更多。
顺便说一句,在这两个示例中,您还将注意到,即使包含函数函数已经运行,内部词汇范围内的变量仍然存在。这称为闭包,指的是嵌套函数对外部变量的访问,即使外部函数已经完成。JavaScript需要足够聪明,以确定这些变量是否不再需要,如果不需要,可以垃圾收集它们。
在这个问题上,我们可以从另一个角度出发,后退一步,看看范围界定在更大的解释框架(运行程序)中的作用。换句话说,假设您正在为一种语言构建一个解释器(或编译器),并负责计算输出,给定一个程序和一些输入。
解释包括跟踪三件事:
状态-即堆和堆栈上的变量和引用内存位置。对该状态的操作,即程序中的每一行代码给定操作运行的环境,即状态在操作上的投影。
解释器从程序中的第一行代码开始,计算其环境,在该环境中运行该行,并捕获其对程序状态的影响。然后,它遵循程序的控制流执行下一行代码,并重复该过程直到程序结束。
为任何操作计算环境的方式都是通过编程语言定义的一组正式规则。术语“绑定”经常用于描述程序的整体状态到环境中的值的映射。注意,我们所说的“总体状态”不是指全局状态,而是指在执行过程中的任何一点上每个可到达定义的总和)。
这是定义范围问题的框架。现在进入下一部分我们的选择。
作为解释器的实现者,您可以通过使环境尽可能接近程序的状态来简化任务。因此,无论前一行是赋值、函数调用、从函数返回还是控制结构(如while循环),一行代码的环境都将简单地由前一行代码环境定义,并将该操作的效果应用于该行代码。
这就是动态作用域的要点,其中任何代码运行的环境都绑定到由其执行上下文定义的程序状态。
或者,你可以想象一个程序员使用你的语言,简化他或她的跟踪变量取值的任务。关于过去执行的全部结果的推理涉及太多的路径和太多的复杂性。词法作用域通过将当前环境限制在当前块、函数或其他作用域单元及其父级(即包围当前时钟的块或调用当前函数的函数)中定义的状态部分来帮助实现这一点。
换句话说,使用词法作用域,任何代码所看到的环境都绑定到与语言中明确定义的作用域(如块或函数)相关联的状态。
范围定义了函数、变量等可用的区域。例如,变量的可用性是在其上下文中定义的,比如函数、文件或对象,它们是在中定义的。我们通常称这些局部变量。
词法部分意味着你可以从阅读源代码中获得范围。
词汇范围也称为静态范围。
动态范围定义了全局变量,这些变量在定义后可以从任何地方调用或引用。有时它们被称为全局变量,尽管大多数编程语言中的全局变量都属于词汇范围。这意味着,可以通过读取代码得出变量在此上下文中可用。也许必须遵循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('MyClass')的代码将得到结果或不得到结果。对RegisterClass()的调用不必在使用GetClass()单元的词法范围内;
动态范围的另一种可能性是Delphi2009中的匿名方法(闭包),因为它们知道调用函数的变量。它不会递归地遵循调用路径,因此不是完全动态的。
推荐文章
- 如何使用Jest测试对象键和值是否相等?
- 将长模板文字行换行为多行,而无需在字符串中创建新行
- 如何在JavaScript中映射/减少/过滤一个集?
- Bower: ENOGIT Git未安装或不在PATH中
- 添加javascript选项选择
- 在Node.js中克隆对象
- 为什么在JavaScript的Date构造函数中month参数的范围从0到11 ?
- 使用JavaScript更改URL参数并指定默认值
- 在window.setTimeout()发生之前取消/终止
- 如何删除未定义和空值从一个对象使用lodash?
- 检测当用户滚动到底部的div与jQuery
- 在JavaScript中检查字符串包含另一个子字符串的最快方法?
- 检测视口方向,如果方向是纵向显示警告消息通知用户的指示
- ASP。NET MVC 3 Razor:在head标签中包含JavaScript文件
- 禁用从HTML页面中拖动图像