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


当前回答

围绕词汇和动态作用域的对话中有一个重要的部分是缺失的:作用域变量的生存期或何时可以访问变量的简单解释。

动态范围界定与“全局”范围界定在我们传统的思考方式中只是非常松散地对应(我之所以将两者进行比较,是因为已经提到了这一点——我并不特别喜欢链接文章的解释);我们最好不要在全局变量和动态变量之间进行比较,尽管根据链接文章的说法,“……[它]作为全局范围变量的替代品很有用。”

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

在上面的答案中,词汇范围定义得很好:词汇范围变量在定义它的函数的本地级别是可用的,或者是可访问的。

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

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

…[I]n动态作用域(或动态作用域),如果变量名的作用域是则其范围是函数正在执行:当函数运行时,变量name存在,并绑定到其变量,但在函数之后返回,变量名不存在。

其他回答

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

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

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

这意味着孩子的函数在词汇上与父母的执行上下文绑定。

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

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不能被其父代访问。

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

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

来源

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

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

本主题与内置绑定函数密切相关,并在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"