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


当前回答

词法范围:在函数外部声明的变量是全局变量,在JavaScript程序中随处可见。在函数内声明的变量具有函数范围,并且仅对该函数内出现的代码可见。

其他回答

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

词汇范围意味着函数在其定义的上下文中查找变量,而不是在其周围的范围中查找变量。

如果您想了解更多详细信息,请查看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

作用域是可访问变量/绑定的上下文。词法范围是指封闭词法块的局部范围,而不是全局范围。

我通常通过举例学习,这里有一点小意思:

const lives = 0;

function catCircus () {
    this.lives = 1;
    const lives = 2;

    const cat1 = {
        lives: 5,
        jumps: () => {
            console.log(this.lives);
        }
    };
    cat1.jumps(); // 1
    console.log(cat1); // { lives: 5, jumps: [Function: jumps] }

    const cat2 = {
        lives: 5,
        jumps: () => {
            console.log(lives);
        }
    };
    cat2.jumps(); // 2
    console.log(cat2); // { lives: 5, jumps: [Function: jumps] }

    const cat3 = {
        lives: 5,
        jumps: () => {
            const lives = 3;
            console.log(lives);
        }
    };
    cat3.jumps(); // 3
    console.log(cat3); // { lives: 5, jumps: [Function: jumps] }

    const cat4 = {
        lives: 5,
        jumps: function () {
            console.log(lives);
        }
    };
    cat4.jumps(); // 2
    console.log(cat4); // { lives: 5, jumps: [Function: jumps] }

    const cat5 = {
        lives: 5,
        jumps: function () {
            var lives = 4;
            console.log(lives);
        }
    };
    cat5.jumps(); // 4
    console.log(cat5); // { lives: 5, jumps: [Function: jumps] }

    const cat6 = {
        lives: 5,
        jumps: function () {
            console.log(this.lives);
        }
    };
    cat6.jumps(); // 5
    console.log(cat6); // { lives: 5, jumps: [Function: jumps] }

    const cat7 = {
        lives: 5,
        jumps: function thrownOutOfWindow () {
            console.log(this.lives);
        }
    };
    cat7.jumps(); // 5
    console.log(cat7); // { lives: 5, jumps: [Function: thrownOutOfWindow] }
}

catCircus();

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

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

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

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