什么是词法范围?

发布于 2024-07-25 23:02:27 字数 21 浏览 12 评论 0原文

词法作用域的简要介绍是什么?

What is a brief introduction to lexical scoping?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(22

暮倦 2024-08-01 23:02:27

我通过例子来理解它们。 :)

首先,词法作用域(也称为静态作用域),采用类似 C 的语法:

void fun()
{
    int x = 5;

    void fun2()
    {
        printf("%d", x);
    }
}

每个内部级别都可以访问其外部级别。

还有另一种方法,称为动态作用域,由 的第一个实现使用Lisp,同样采用类似 C 的语法:

void fun()
{
    printf("%d", x);
}

void dummy1()
{
    int x = 5;

    fun();
}

void dummy2()
{
    int x = 10;

    fun();
}

这里 fun 可以访问 dummy1dummy2 中的 x code> 或任何调用 fun 并声明了 x 的函数中的任何 x

dummy1();

将打印 5,

dummy2();

将打印 10。

第一个称为静态,因为它可以在编译时推导,第二个称为动态,因为外部作用域是动态的并且取决于函数的链调用。

我发现静态范围更容易观察。 大多数语言最终都会走这条路,甚至 Lisp(两者都可以,对吧?)。 动态作用域就像将所有变量的引用传递给被调用的函数。

作为编译器无法推断函数的外部动态范围的示例,请考虑我们的最后一个示例。 如果我们这样写:

if(/* some condition */)
    dummy1();
else
    dummy2();

调用链取决于运行时条件。 如果为 true,则调用链如下所示:

dummy1 --> fun()

如果条件为 false:

dummy2 --> fun()

两种情况下 fun 的外部作用域都是调用者加上调用者的调用者,依此类推。

需要提一下的是,C 语言不允许嵌套函数,也不允许动态作用域。

I understand them through examples. :)

First, lexical scope (also called static scope), in C-like syntax:

void fun()
{
    int x = 5;

    void fun2()
    {
        printf("%d", x);
    }
}

Every inner level can access its outer levels.

There is another way, called dynamic scope used by the first implementation of Lisp, again in a C-like syntax:

void fun()
{
    printf("%d", x);
}

void dummy1()
{
    int x = 5;

    fun();
}

void dummy2()
{
    int x = 10;

    fun();
}

Here fun can either access x in dummy1 or dummy2, or any x in any function that call fun with x declared in it.

dummy1();

will print 5,

dummy2();

will print 10.

The first one is called static because it can be deduced at compile-time, and the second is called dynamic because the outer scope is dynamic and depends on the chain call of the functions.

I find static scoping easier for the eye. Most languages went this way eventually, even Lisp (can do both, right?). Dynamic scoping is like passing references of all variables to the called function.

As an example of why the compiler can not deduce the outer dynamic scope of a function, consider our last example. If we write something like this:

if(/* some condition */)
    dummy1();
else
    dummy2();

The call chain depends on a run time condition. If it is true, then the call chain looks like:

dummy1 --> fun()

If the condition is false:

dummy2 --> fun()

The outer scope of fun in both cases is the caller plus the caller of the caller and so on.

Just to mention that the C language does not allow nested functions nor dynamic scoping.

鼻尖触碰 2024-08-01 23:02:27

让我们尝试尽可能短的定义:

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

这就是全部内容了!

Lets try the shortest possible definition:

Lexical Scoping defines how variable names are resolved in nested functions: inner functions contain the scope of parent functions even if the parent function has returned.

That is all there is to it!

不美如何 2024-08-01 23:02:27
var scope = "I am global";
function whatismyscope(){
   var scope = "I am just a local";
   function func() {return scope;}
   return func;
}

whatismyscope()()

上面的代码将返回“I am just a local”。 它不会返回“我是全局的”。 因为函数 func() 计算的是最初定义的位置,该函数位于函数 Whatismyscope 的范围内。

它不会受到任何调用的影响(全局作用域/甚至来自另一个函数内),这就是为什么全局作用域值 I am global 不会被打印。

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

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

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

whatismyscope()()

The above code will return "I am just a local". It will not return "I am a global". Because the function func() counts where is was originally defined which is under the scope of function whatismyscope.

It will not bother from whatever it is being called(the global scope/from within another function even), that's why global scope value I am global will not be printed.

This is called lexical scoping where "functions are executed using the scope chain that was in effect when they were defined" - according to JavaScript Definition Guide.

Lexical scope is a very very powerful concept.

流云如水 2024-08-01 23:02:27

词法(又称为静态)作用域是指仅根据变量在代码文本语料库中的位置来确定变量的作用域。 变量始终引用其顶级环境。 最好从与动态作用域的关系来理解它。

Lexical (AKA static) scoping refers to determining a variable's scope based solely on its position within the textual corpus of code. A variable always refers to its top-level environment. It's good to understand it in relation to dynamic scope.

被你宠の有点坏 2024-08-01 23:02:27

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

词法部分意味着您可以通过阅读源代码来推导出范围。

词法作用域也称为静态作用域。

动态作用域定义了全局变量,这些变量在定义后可以从任何地方调用或引用。 有时它们被称为全局变量,尽管大多数编程语言中的全局变量都具有词法范围。 这意味着,可以通过阅读代码得出该变量在此上下文中可用。 也许必须遵循使用或包含子句才能找到实例或定义,但代码/编译器知道这个地方的变量。

相比之下,在动态作用域中,您首先在本地函数中搜索,然后在调用本地函数的函数中搜索,然后在调用该函数的函数中搜索,依此类推,在调用堆栈中搜索。 “动态”指的是变化,因为每次调用给定函数时,调用堆栈都可能不同,因此该函数可能会根据调用位置而命中不同的变量。 (请参阅此处

要查看动态作用域的有趣示例,请参阅此处

有关更多详细信息,请参阅此处此处

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') 的代码将得到结果与否。 对 RegisterClass() 的调用不必在使用 GetClass() 的单元的词法范围内;

动态作用域的另一种可能性是匿名方法(闭包)在 Delphi 2009 中,因为他们知道调用函数的变量。 它不会递归地遵循从那里开始的调用路径,因此不是完全动态的。

Scope defines the area, where functions, variables and such are available. The availability of a variable for example is defined within its the context, let's say the function, file, or object, they are defined in. We usually call these local variables.

The lexical part means that you can derive the scope from reading the source code.

Lexical scope is also known as static scope.

Dynamic scope defines global variables that can be called or referenced from anywhere after being defined. Sometimes they are called global variables, even though global variables in most programmin languages are of lexical scope. This means, it can be derived from reading the code that the variable is available in this context. Maybe one has to follow a uses or includes clause to find the instatiation or definition, but the code/compiler knows about the variable in this place.

In dynamic scoping, by contrast, you search in the local function first, then you search in the function that called the local function, then you search in the function that called that function, and so on, up the call stack. "Dynamic" refers to change, in that the call stack can be different every time a given function is called, and so the function might hit different variables depending on where it is called from. (see here)

To see an interesting example for dynamic scope see here.

For further details see here and here.

Some examples in Delphi/Object Pascal

Delphi has lexical scope.

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.

The closest Delphi gets to dynamic scope is the RegisterClass()/GetClass() function pair. For its use see here.

Let's say that the time RegisterClass([TmyClass]) is called to register a certain class cannot be predicted by reading the code (it gets called in a button click method called by the user), code calling GetClass('TmyClass') will get a result or not. The call to RegisterClass() does not have to be in the lexical scope of the unit using GetClass();

Another possibility for dynamic scope are anonymous methods (closures) in Delphi 2009, as they know the variables of their calling function. It does not follow the calling path from there recursively and therefore is not fully dynamic.

忘羡 2024-08-01 23:02:27

词法作用域意味着在嵌套的函数组中,内部函数可以访问其父作用域的变量和其他资源

这意味着子函数在词法上绑定到其父函数的执行上下文。

词法作用域有时也称为静态作用域。

function grandfather() {
    var name = 'Hammad';
    // 'likes' is not accessible here
    function parent() {
        // 'name' is accessible here
        // 'likes' is not accessible here
        function child() {
            // Innermost level of the scope chain
            // 'name' is also accessible here
            var likes = 'Coding';
        }
    }
}

关于词法作用域,您会注意到它是向前工作的,这意味着该名称可以由其子级执行上下文访问。

但它不能向后工作到其父级,这意味着变量 likes 无法被其父级访问。

这也告诉我们,在不同的执行中具有相同名称的变量上下文从执行堆栈的顶部到底部获得优先级。

最内层函数(执行堆栈的最顶层上下文)中名称与另一个变量相似的变量将具有更高的优先级。

来源

Lexical scope means that in a nested group of functions, the inner functions have access to the variables and other resources of their parent scope.

This means that the child's functions are lexically bound to the execution context of their parents.

Lexical scope is sometimes also referred to as static scope.

function grandfather() {
    var name = 'Hammad';
    // 'likes' is not accessible here
    function parent() {
        // 'name' is accessible here
        // 'likes' is not accessible here
        function child() {
            // Innermost level of the scope chain
            // 'name' is also accessible here
            var likes = 'Coding';
        }
    }
}

The thing you will notice about lexical scope is that it works forward, meaning the name can be accessed by its children's execution contexts.

But it doesn't work backward to its parents, meaning that the variable likes cannot be accessed by its parents.

This also tells us that variables having the same name in different execution contexts gain precedence from top to bottom of the execution stack.

A variable, having a name similar to another variable, in the innermost function (topmost context of the execution stack) will have higher precedence.

Source.

哑剧 2024-08-01 23:02:27

JavaScript 中的词法作用域意味着在函数外部定义的变量可以在变量声明后定义的另一个函数内部访问。 但反之则不然。 函数内部定义的变量将无法在该函数外部访问。

这个概念在 JavaScript 的闭包中大量使用。

假设我们有以下代码。

var x = 2;
var add = function() {
    var y = 1;
    return x + y;
};

现在,当你调用 add() --> 这将打印 3。

因此,add() 函数正在访问在方法函数 add 之前定义的全局变量 x 。 这是由于 JavaScript 中的词法作用域而被调用的。

A lexical scope in JavaScript means that a variable defined outside a function can be accessible inside another function defined after the variable declaration. But the opposite is not true; the variables defined inside a function will not be accessible outside that function.

This concept is heavily used in closures in JavaScript.

Let's say we have the below code.

var x = 2;
var add = function() {
    var y = 1;
    return x + y;
};

Now, when you call add() --> this will print 3.

So, the add() function is accessing the global variable x which is defined before method function add. This is called due to lexical scoping in JavaScript.

落墨 2024-08-01 23:02:27

我喜欢 @Arak 等人提供的功能齐全、与语言无关的答案。 由于这个问题被标记为JavaScript,所以我想添加一些针对该语言的注释。

在 JavaScript 中,我们对作用域的选择是:

  • 按原样(无作用域调整)
  • 词法 var _this = this; 函数回调(){ console.log(_this); }
  • 绑定 callback.bind(this)

我认为值得注意的是 JavaScript 实际上没有动态作用域.bind 调整 this 关键字,这很接近,但技术上并不相同。

这是演示这两种方法的示例。 每次决定如何确定回调的范围时,您都会执行此操作,因此这适用于 Promise、事件处理程序等。

词法

这就是 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`
  }
//...

这些方法,只要我知道,行为上是等价的。

I love the fully featured, language-agnostic answers from folks like @Arak. Since this question was tagged JavaScript though, I'd like to chip in some notes very specific to this language.

In JavaScript our choices for scoping are:

  • as-is (no scope adjustment)
  • lexical var _this = this; function callback(){ console.log(_this); }
  • bound callback.bind(this)

It's worth noting, I think, that JavaScript doesn't really have dynamic scoping. .bind adjusts the this keyword, and that's close, but not technically the same.

Here is an example demonstrating both approaches. You do this every time you make a decision about how to scope callbacks so this applies to promises, event handlers, and more.

Lexical

Here is what you might term Lexical Scoping of callbacks in 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
  }
  //...
};

Bound

Another way to scope is to use Function.prototype.bind:

var downloadManager = {
  initialize: function() {
    $('.downloadLink').on('click', function () {
      this.startDownload();
    }.bind(this)); // Create a function object bound to `this`
  }
//...

These methods are, as far as I know, behaviorally equivalent.

彩扇题诗 2024-08-01 23:02:27

用简单的语言来说,词法作用域是在您的作用域之外定义的变量,或者上层作用域在您的作用域内自动可用,这意味着您不需要将其传递到那里。

示例:

let str="JavaScript";

const myFun = () => {
    console.log(str);
}

myFun();

// 输出:JavaScript

In simple language, lexical scope is a variable defined outside your scope or upper scope is automatically available inside your scope which means you don't need to pass it there.

Example:

let str="JavaScript";

const myFun = () => {
    console.log(str);
}

myFun();

// Output: JavaScript

茶色山野 2024-08-01 23:02:27

词法作用域意味着函数在定义它的上下文中查找变量,而不是在它周围的作用域中查找变量。

如果您想了解更多详细信息,请查看 Lisp 中词法作用域的工作原理。 Kyle Cronin 在 Common Lisp 中的动态和词法变量<中选择的答案/a> 比这里的答案清楚得多。

巧合的是,我只在 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

Lexical scope means that a function looks up variables in the context where it was defined, and not in the scope immediately around it.

Look at how lexical scope works in Lisp if you want more detail. The selected answer by Kyle Cronin in Dynamic and Lexical variables in Common Lisp is a lot clearer than the answers here.

Coincidentally I only learned about this in a Lisp class, and it happens to apply in JavaScript as well.

I ran this code in Chrome's console.

// 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)
})();                     //)

Output:

5
10
5
初相遇 2024-08-01 23:02:27

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

Lexical scoping: Variables declared outside of a function are global variables and are visible everywhere in a JavaScript program. Variables declared inside a function have function scope and are visible only to code that appears inside that function.

不交电费瞎发啥光 2024-08-01 23:02:27

IBM 将其定义为:

程序或段单元中包含声明的部分
适用。 例程中声明的标识符在该例程中是已知的
例程以及所有嵌套例程内。 如果嵌套例程声明
具有相同名称的项目,外部项目在
嵌套例程。

示例 1:

function x() {
    /*
    Variable 'a' is only available to function 'x' and function 'y'.
    In other words the area defined by 'x' is the lexical scope of
    variable 'a'
    */
    var a = "I am a";

    function y() {
        console.log( a )
    }
    y();

}
// outputs 'I am a'
x();

示例 2:

function x() {

    var a = "I am a";

    function y() {
         /*
         If a nested routine declares an item with the same name,
         the outer item is not available in the nested routine.
         */
        var a = 'I am inner a';
        console.log( a )
    }
    y();

}
// outputs 'I am inner a'
x();

IBM defines it as:

The portion of a program or segment unit in which a declaration
applies. An identifier declared in a routine is known within that
routine and within all nested routines. If a nested routine declares
an item with the same name, the outer item is not available in the
nested routine.

Example 1:

function x() {
    /*
    Variable 'a' is only available to function 'x' and function 'y'.
    In other words the area defined by 'x' is the lexical scope of
    variable 'a'
    */
    var a = "I am a";

    function y() {
        console.log( a )
    }
    y();

}
// outputs 'I am a'
x();

Example 2:

function x() {

    var a = "I am a";

    function y() {
         /*
         If a nested routine declares an item with the same name,
         the outer item is not available in the nested routine.
         */
        var a = 'I am inner a';
        console.log( a )
    }
    y();

}
// outputs 'I am inner a'
x();
浸婚纱 2024-08-01 23:02:27

我希望这对您有所帮助,这是我尝试稍微更抽象的定义:

词法范围
某事物(例如函数或变量)对程序中其他元素的访问或范围由其在源代码中的位置决定。

Fwiw,我这里的逻辑只是建立在以下定义之上:

词汇:与语言的单词或词汇相关(特别是与语法或结构分开的单词){在我们的例子中 - 一种编程语言}。

范围(名词):操作范围{在我们的例子中范围是:可以访问的内容}。

请注意,1960 年定义词法范围来自 ALGOL 60 规范的内容比我上面的尝试要简洁得多:

词法范围:应用名称与实体的绑定的源代码部分。 来源

I hope this is helpful, here is my attempt at a slightly more abstract definition:

Lexical scope:
The access or range something (e.g. a function or variable) has to other elements in the program as determined by its position in the source code.

Fwiw, my logic here simply builds from the definitions of:

Lexical: relating to the words or vocabulary of a language (specifically the word as separate from it's grammar or construction) {in our case - a programming language}.

Scope (noun): the range of operation {in our case the range is: what can be accessed}.

Note, the original 1960 definition of Lexical Scope from the ALGOL 60 spec is far more pithy than my attempt above:

Lexical scope: the portion of source code in which a binding of a name with an entity applies. source

陌若浮生 2024-08-01 23:02:27

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

- global execution context
    - foo
    - bar
    - function1 execution context
        - foo2
        - bar2
        - function2 execution context
            - foo3
            - bar3

foobar 始终位于可用标识符的词典中,因为它们是全局的。

当执行 function1 时,它可以访问 foo2bar2foo词典栏

当执行function2时,它可以访问foo3bar3foo2bar2的词典foobar

全局和/或外部函数无法访问内部函数标识符的原因是因为该函数的执行尚未发生,因此其标识符尚未分配给内存。 更重要的是,一旦内部上下文执行,它就会从执行堆栈中删除,这意味着它的所有标识符都已被垃圾收集并且不再可用。

最后,这就是为什么嵌套执行上下文始终可以访问其祖先执行上下文,从而可以访问更大的标识符词典的原因。

请参阅:

特别感谢 @robr3rd 寻求帮助简化上述定义。

Lexical scope refers to the lexicon of identifiers (e.g., variables, functions, etc.) visible from the current position in the execution stack.

- global execution context
    - foo
    - bar
    - function1 execution context
        - foo2
        - bar2
        - function2 execution context
            - foo3
            - bar3

foo and bar are always within the lexicon of available identifiers because they are global.

When function1 is executed, it has access to a lexicon of foo2, bar2, foo, and bar.

When function2 is executed, it has access to a lexicon of foo3, bar3, foo2, bar2, foo, and bar.

The reason global and/or outer functions do not have access to an inner functions identifiers is because the execution of that function has not occurred yet and therefore, none of its identifiers have been allocated to memory. What’s more, once that inner context executes, it is removed from the execution stack, meaning that all of it’s identifiers have been garbage collected and are no longer available.

Finally, this is why a nested execution context can ALWAYS access it’s ancestors execution context and thus why it has access to a greater lexicon of identifiers.

See:

Special thanks to @robr3rd for help simplifying the above definition.

丶视觉 2024-08-01 23:02:27

围绕词法动态作用域的对话中缺少一个重要部分:对作用域变量生命周期的简单解释 - 或者何时可以访问变量。

动态作用域只是非常松散地对应于我们传统上认为的“全局”作用域(我提出两者之间比较的原因是它已经提到 - 我不太喜欢链接文章的解释); 最好我们不要在全局变量和动态变量之间进行比较——尽管根据链接的文章,“...[它]可以作为全局范围变量的替代品。”

那么,简单来说,这两种范围界定机制之间的重要区别是什么?

词法作用域在上面的答案中已经被很好地定义了:词法作用域变量在定义它的函数的本地级别是可用的,或者是可访问的。

然而,由于它不是OP的重点,动态范围界定并没有受到很大的关注,而它所受到的关注意味着它可能需要更多的关注(这不是对其他答案的批评,而是“哦,这个答案让我们希望还有更多”)。 因此,这里还有更多内容:

动态作用域意味着在函数调用的生命周期内(或者在函数执行时),较大的程序可以访问变量。 确实,维基百科实际上在差异解释方面做得很好 两者之间。 为了不混淆它,下面是描述动态作用域的文本:

...[I]在动态作用域(或动态作用域)中,如果变量名的作用域是
某个函数,那么它的作用域就是这个时间段
函数正在执行:当函数运行时,变量
名称存在,并且绑定到它的变量,但是在函数之后
返回,变量名不存在。

There is an important part of the conversation surrounding lexical and dynamic scoping that is missing: a plain explanation of the lifetime of the scoped variable - or when the variable can be accessed.

Dynamic scoping only very loosely corresponds to "global" scoping in the way that we traditionally think about it (the reason I bring up the comparison between the two is that it has already been mentioned - and I don't particularly like the linked article's explanation); it is probably best we don't make the comparison between global and dynamic - though supposedly, according to the linked article, "...[it] is useful as a substitute for globally scoped variables."

So, in plain English, what's the important distinction between the two scoping mechanisms?

Lexical scoping has been defined very well throughout the answers above: lexically scoped variables are available - or, accessible - at the local level of the function in which it was defined.

However - as it is not the focus of the OP - dynamic scoping has not received a great deal of attention and the attention it has received means it probably needs a bit more (that's not a criticism of other answers, but rather a "oh, that answer made we wish there was a bit more"). So, here's a little bit more:

Dynamic scoping means that a variable is accessible to the larger program during the lifetime of the function call - or, while the function is executing. Really, Wikipedia actually does a nice job with the explanation of the difference between the two. So as not to obfuscate it, here is the text that describes dynamic scoping:

...[I]n dynamic scoping (or dynamic scope), if a variable name's scope is a
certain function, then its scope is the time-period during which the
function is executing: while the function is running, the variable
name exists, and is bound to its variable, but after the function
returns, the variable name does not exist.

笔落惊风雨 2024-08-01 23:02:27

古老的问题,但这是我的看法。

词法(静态)作用域是指源代码中变量的作用域。

在像 JavaScript 这样的语言中,函数可以传递、附加和重新附加到各种对象,您可能认为该范围取决于当时调用该函数的人,但事实并非如此。 以这种方式更改作用域将是动态作用域,而 JavaScript 不会这样做,除非可能使用 this 对象引用。

为了说明这一点:

var a='apple';

function doit() {
    var a='aardvark';
    return function() {
        alert(a);
    }
}

var test=doit();
test();

在示例中,变量 a 是全局定义的,但隐藏在 doit() 函数中。 该函数返回另一个函数,如您所见,该函数依赖于其自身范围之外的 a 变量。

如果你运行这个,你会发现使用的值是 aardvark,而不是 apple,尽管它在 test() 的范围内> function,不在原始函数的词法范围内。 也就是说,使用的作用域是源代码中出现的作用域,而不是函数实际使用的作用域。

这一事实可能会产生令人烦恼的后果。 例如,您可能认为单独组织函数更容易,然后在需要时使用它们,例如在事件处理程序中:

var a='apple',b='banana';

function init() {
  var a='aardvark',b='bandicoot';
  document.querySelector('button#a').onclick=function(event) {
    alert(a);
  }
  document.querySelector('button#b').onclick=doB;
}

function doB(event) {
  alert(b);
}

init();
<button id="a">A</button>
<button id="b">B</button>

此代码示例执行其中一项操作。 您可以看到,由于词法作用域的原因,按钮 A 使用内部变量,而按钮 B 则不使用。 您最终可能会嵌套更多的函数,超出您的预期。

顺便说一句,在这两个示例中,您还会注意到,即使包含函数的函数已经运行完毕,内部词法作用域变量仍然存在。 这称为闭包,指的是嵌套函数对外部变量的访问,即使外部函数已经完成。 JavaScript 需要足够智能来确定这些变量是否不再需要,如果不需要,可以对它们进行垃圾回收。

Ancient question, but here is my take on it.

Lexical (static) scope refers to the scope of a variable in the source code.

In a language like JavaScript, where functions can be passed around and attached and re-attached to miscellaneous objects, you might have though that scope would depend on who’s calling the function at the time, but it doesn’t. Changing the scope that way would be dynamic scope, and JavaScript doesn’t do that, except possibly with the this object reference.

To illustrate the point:

var a='apple';

function doit() {
    var a='aardvark';
    return function() {
        alert(a);
    }
}

var test=doit();
test();

In the example, the variable a is defined globally, but shadowed in the doit() function. This function returns another function which, as you see, relies on the a variable outside of its own scope.

If you run this, you will find that the value used is aardvark, not apple which, though it is in the scope of the test() function, is not in the lexical scope of the original function. That is, the scope used is the scope as it appears in the source code, not the scope where the function is actually used.

This fact can have annoying consequences. For example, you might decide that it’s easier to organise your functions separately, and then use them when the time comes, such as in an event handler:

var a='apple',b='banana';

function init() {
  var a='aardvark',b='bandicoot';
  document.querySelector('button#a').onclick=function(event) {
    alert(a);
  }
  document.querySelector('button#b').onclick=doB;
}

function doB(event) {
  alert(b);
}

init();
<button id="a">A</button>
<button id="b">B</button>

This code sample does one of each. You can see that because of lexical scoping, button A uses the inner variable, while button B doesn’t. You may end up nesting functions more than you would have liked.

By the way, in both examples, you will also notice that the inner lexically scoped variables persist even though the containing function function has run its course. This is called closure, and refers to a nested function’s access to outer variables, even if the outer function has finished. JavaScript needs to be smart enough to determine whether those variables are no longer needed, and if not, can garbage collect them.

初心未许 2024-08-01 23:02:27

通过退后一步并查看范围界定在更大的解释框架(运行程序)中的作用,我们可以得到关于这个问题的不同角度。 换句话说,想象一下您正在为一种语言构建一个解释器(或编译器),并负责在给定程序和一些输入的情况下计算输出。

解释涉及跟踪三件事:

  1. 状态 - 即堆和堆栈上的变量和引用的内存位置。

  2. 对该状态的操作 - 即程序中的每一行代码

  3. 环境给定操作运行 - 即状态在操作上的投影。

解释器从程序中的第一行代码开始,计算其环境,在该环境中运行该行并捕获其对程序状态的影响。 然后它按照程序的控制流执行下一行代码,并重复该过程直到程序结束。

计算任何操作的环境的方式是通过编程语言定义的一组正式规则。 术语“绑定”经常用于描述程序的整体状态到环境中的值的映射。 请注意,“整体状态”并不是指全局状态,而是指执行过程中任意时刻每个可到达定义的总和。

这是定义范围界定问题的框架。 现在我们的选择是什么的下一部分。

  • 作为解释器的实现者,您可以通过使环境尽可能接近程序的状态来简化您的任务。 因此,一行代码的环境将简单地由前一行代码的环境以及应用于该操作的效果来定义,无论前一行是否是赋值、函数调用、函数返回,或控制结构,例如 while 循环。

这是动态作用域的要点,其中任何代码运行的环境都绑定到由其执行上下文定义的程序状态。

  • 或者,您可以想象一个程序员使用您的语言并简化他或她跟踪变量可以取的值的任务。 在推理过去执行的整体结果时,涉及太多的路径和太多的复杂性。 词法作用域通过将当前环境限制为当前块、函数或其他作用域单元及其父级(即包含该块的块)中定义的状态部分来帮助实现此目的。当前时钟,或调用当前函数的函数)。

换句话说,对于词法作用域,任何代码看到的环境都必须与在语言中显式定义的作用域(例如块或函数)相关联的状态。

Here's a different angle on this question that we can get by taking a step back and looking at the role of scoping in the larger framework of interpretation (running a program). In other words, imagine that you were building an interpreter (or compiler) for a language and were responsible for computing the output, given a program and some input to it.

Interpretation involves keeping track of three things:

  1. State - namely, variables and referenced memory locations on the heap and stack.

  2. Operations on that state - namely, every line of code in your program

  3. The environment in which a given operation runs - namely, the projection of state on an operation.

An interpreter starts at the first line of code in a program, computes its environment, runs the line in that environment and captures its effect on the program's state. It then follows the program's control flow to execute the next line of code, and repeats the process till the program ends.

The way you compute the environment for any operation is through a formal set of rules defined by the programming language. The term "binding" is frequently used to describe the mapping of the overall state of the program to a value in the environment. Note that by "overall state" we do not mean global state, but rather the sum total of every reachable definition, at any point in the execution).

This is the framework in which the scoping problem is defined. Now to the next part of what our options are.

  • As the implementor of the interpreter, you could simplify your task by making the environment as close as possible to the program's state. Accordingly, the environment of a line of code would simply be defined by environment of the previous line of code with the effects of that operation applied to it, regardless of whether the previous line was an assignment, a function call, return from a function, or a control structure such as a while loop.

This is the gist of dynamic scoping, wherein the environment that any code runs in is bound to the state of the program as defined by its execution context.

  • Or, you could think of a programmer using your language and simplify his or her task of keeping track of the values a variable can take. There are way too many paths and too much complexity involved in reasoning about the outcome the totality of past execution. Lexical Scoping helps do this by restricting the current environment to the portion of state defined in the current block, function or other unit of scope, and its parent (i.e. the block enclosing the current clock, or the function that called the present function).

In other words, with lexical scope the environment that any code sees is bound to state associated with a scope defined explicitly in the language, such as a block or a function.

度的依靠╰つ 2024-08-01 23:02:27

范围是可以访问变量/绑定的上下文。 词法范围是指封闭的词法块或块的本地范围,而不是例如全局范围。

Scope is the context within which a variable/binding is accessible. Lexical scope means local to the enclosing lexical block or blocks as opposed to for instance global scope.

怪我太投入 2024-08-01 23:02:27

因此,要了解词法环境,我们首先要了解作用域的概念:

作用域:

因此,每当您在 javascript 中创建函数时,您都会创建一个作用域,这意味着该方法内的变量将无法访问从外面。
JavaScript 中可以有多个作用域。 首先也是最重要的,它具有全球范围。 然后假设您定义了函数 foo ,然后您拥有由 foo 创建的作用域,现在您在 foo 中定义了另一个名为 bar 的方法,然后您有另一个由 barfoo 范围内创建的范围。

function foo(){
let name="foo"
function bar(){
console.log(name)
}
return bar
}

现在,当我们在 bar 内部引用变量 name 时,JavaScript 将不会在 bar 内部找到任何内容,因此 javascript 将查找其外部foo 的范围并找到该变量然后使用它。
这个作用域链称为作用域链。

现在词法环境基本上为您提供了这个作用域链以及一组关于如何使用它解析标识符的规则。 这总共称为函数的词法环境,在运行时用于解析标识符。

请记住,javascript 在编译阶段定义了此作用域,因此称为静态作用域。< /strong>

So to understand the lexical environment, let's understand the concept of scope first:

Scope:

So whenever you create a function in javascript you're creating a scope, Which means variables inside that method won't be accessible from outside.
There can be multiple scopes in javascript. First and foremost there's a global scope. Then let's say you define function foo , Then you have scope created by foo , and now you define another method inside foo by name bar , Then you have another scope created by bar inside the scope of foo.

function foo(){
let name="foo"
function bar(){
console.log(name)
}
return bar
}

Now when we make a reference to a variable name inside bar, Then javascript will not find anything inside bar, So javascript will look into its outer scope of foo and find that variable and then use it.
This chain of scopes is called a scope chain.

So now lexical environment basically provides you with this scope chain and a set of rules on how to resolve an identifier using it. This in total is called lexical environment of a function and is used during runtime to resolve an identifier.

Keeping in mind , javascript defines this scope during the compilation phase and hence is called static scoping.

在你怀里撒娇 2024-08-01 23:02:27

该主题与内置 bind 函数密切相关,并在 ECMAScript 6 中引入 箭头函数。 这真的很烦人,因为对于我们想要使用的每个新“类”(实际上是函数)方法,我们必须绑定它才能访问作用域。

默认情况下,JavaScript 不会在函数上设置其 this 范围(它不会设置 上下文 )。 默认情况下,您必须明确说明您想要的上下文具有。

箭头函数自动获取所谓的词法范围(可以访问其包含块中的变量定义)。 使用 箭头函数 时,它会自动将 this 绑定到该位置其中首先定义了箭头函数,以及 context<此 箭头函数 的 /a> 是其包含块。

通过下面最简单的示例来了解它在实践中是如何工作的。

在箭头函数之前(默认情况下无词法范围):

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"

This topic is strongly related with the built-in bind function and introduced in ECMAScript 6 Arrow Functions. It was really annoying, because for every new "class" (function actually) method we wanted to use, we had to bind this in order to have access to the scope.

JavaScript by default doesn't set its scope of this on functions (it doesn't set the context on this). By default you have to explicitly say which context you want to have.

The arrow functions automatically gets so-called lexical scope (have access to variable's definition in its containing block). When using arrow functions it automatically binds this to the place where the arrow function was defined in the first place, and the context of this arrow functions is its containing block.

See how it works in practice on the simplest examples below.

Before Arrow Functions (no lexical scope by default):

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"

With arrow functions (lexical scope by default):

const programming = {
  language: "JavaScript",
  getLanguage: function() {
    return this.language;
  }
}

const arrowFunction = () => {
    console.log(programming.getLanguage());
}

arrowFunction(); // Output: "JavaScript"
∝单色的世界 2024-08-01 23:02:27

词法作用域意味着函数从定义它们的作用域解析自由变量,而不是从调用它们的作用域解析自由变量。

Lexical scoping means functions resolve free variables from the scope where they were defined, not from the scope where they are called.

九命猫 2024-08-01 23:02:27

我通常通过例子来学习,这里有一些东西:

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();

I normally learn by example, and here's a little something:

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();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文