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


当前回答

我喜欢@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`
  }
//...

据我所知,这些方法在行为上是等效的。

其他回答

var scope = "I am global";
function whatismyscope(){
   var scope = "I am just a local";
   function func() {return scope;}
   return func;
}

whatismyscope()()

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

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

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

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

词汇(AKA静态)作用域是指仅基于变量在代码文本语料库中的位置来确定变量的作用域。变量始终引用其顶级环境。了解它与动态范围的关系是很好的。

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

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

词汇范围也称为静态范围。

动态范围定义了全局变量,这些变量在定义后可以从任何地方调用或引用。有时它们被称为全局变量,尽管大多数编程语言中的全局变量都属于词汇范围。这意味着,可以通过读取代码得出变量在此上下文中可用。也许必须遵循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中的匿名方法(闭包),因为它们知道调用函数的变量。它不会递归地遵循调用路径,因此不是完全动态的。

让我们尝试最短的定义:

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

这就是它的全部!

词汇范围是指从执行堆栈中的当前位置可见的标识符(例如,变量、函数等)的词汇。

- 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帮助简化上述定义。