词汇范围的简要介绍是什么?
当前回答
我通过例子来理解它们。:)
首先,词法作用域(也称为静态作用域),在类似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可以访问dummy1或dummy2中的x,也可以访问任何函数中声明了x的调用fun的任何x。
dummy1();
将打印5,
dummy2();
将打印10。
第一个被称为静态的,因为它可以在编译时推导出来;第二个被称之为动态的,因为外部范围是动态的,并且取决于函数的链调用。
我发现静态观察对眼睛来说更容易。大多数语言最终都走上了这条路,甚至Lisp(两者都能做到,对吧?)。动态作用域类似于将所有变量的引用传递给被调用函数。
作为编译器无法推断函数的外部动态范围的示例,请考虑我们的最后一个示例。如果我们这样写:
if(/* some condition */)
dummy1();
else
dummy2();
调用链取决于运行时条件。如果为真,则调用链看起来像:
dummy1 --> fun()
如果条件为假:
dummy2 --> fun()
在这两种情况下,乐趣的外部范围都是调用者加上调用者的调用者等等。
只需一提,C语言既不允许嵌套函数,也不允许动态作用域。
其他回答
本主题与内置绑定函数密切相关,并在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"
在简单的语言中,词法作用域是在作用域外部定义的变量,或者更高的作用域在作用域内部自动可用,这意味着您不需要在那里传递它。
例子:
let str="JavaScript";
const myFun = () => {
console.log(str);
}
myFun();
//输出: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需要足够聪明,以确定这些变量是否不再需要,如果不需要,可以垃圾收集它们。
围绕词汇和动态作用域的对话中有一个重要的部分是缺失的:作用域变量的生存期或何时可以访问变量的简单解释。
动态范围界定与“全局”范围界定在我们传统的思考方式中只是非常松散地对应(我之所以将两者进行比较,是因为已经提到了这一点——我并不特别喜欢链接文章的解释);我们最好不要在全局变量和动态变量之间进行比较,尽管根据链接文章的说法,“……[它]作为全局范围变量的替代品很有用。”
那么,用简单的英语来说,这两种范围界定机制之间的重要区别是什么?
在上面的答案中,词汇范围定义得很好:词汇范围变量在定义它的函数的本地级别是可用的,或者是可访问的。
然而,由于它不是OP的重点,动态范围界定并没有得到太多的关注,它得到的关注意味着它可能需要更多的关注(这不是对其他答案的批评,而是“哦,那个答案让我们希望有更多的关注”)。所以,这里还有一点:
动态作用域意味着在函数调用的生命周期内,或在函数执行时,较大的程序可以访问变量。实际上,维基百科在解释两者之间的差异方面做得很好。为了避免混淆,下面是描述动态范围的文本:
…[I]n动态作用域(或动态作用域),如果变量名的作用域是则其范围是函数正在执行:当函数运行时,变量name存在,并绑定到其变量,但在函数之后返回,变量名不存在。
JavaScript中的词法作用域意味着在函数外部定义的变量可以在变量声明之后定义的另一个函数内部访问。但事实并非相反;在函数内部定义的变量在该函数外部无法访问。
这个概念在JavaScript中的闭包中大量使用。
假设我们有以下代码。
var x = 2;
var add = function() {
var y = 1;
return x + y;
};
现在,当您调用add()-->时,这将打印3。
因此,add()函数正在访问在方法函数add之前定义的全局变量x。这是由于JavaScript中的词法作用域而调用的。