JavaScript中的词汇范围 - JS中的范围到底是什么?

276 阅读8分钟

术语 "词汇范围"乍一看似乎很棘手,难以掌握。但了解每个词的含义是很有帮助的。

因此,本文将通过首先研究 "词法 "和 "范围 "的含义来解释词法范围。

所以,让我们从了解 "范围 "这个词开始。

究竟什么是范围?

范围是指一个项目(如函数或变量)对其他代码可见和可访问的区域

注意

  • Scope指的是区域、空间或地区。
  • 全局范围 指的是全局空间或一个公共空间。
  • 本地范围 指的是本地区域或一个受限制的区域。

这里有一个例子

// Define a variable in the global scope:
const fullName = "Oluwatobi Sofela";

// Define nested functions:
function profile() {
  function sayName() {
    function writeName() {
      return fullName;
    }
    return writeName();
  }
  return sayName();
}

在上面的代码段中,我们在全局范围内定义了fullName 这个变量。这意味着它对脚本中的所有代码都是可见的、可访问的。

但是我们在sayName() 函数中定义了writeName() ,所以它在本地的作用域是sayName()

换句话说,writeName() 在本地是可见的,只有sayName() 函数中的代码可以访问。

请记住,每当writeName() 函数被调用时,计算机不会直接进入全局范围来调用fullName 变量。相反,它必须依次通过作用域链来寻找fullName

什么是作用域链?

作用域链 是指从变量被调用 的作用域到全局作用域之间存在的唯一空间。

这里有一个例子

// Define a variable in the global scope:
const fullName = "Oluwatobi Sofela";

// Define nested functions:
function profile() {
  function sayName() {
    function writeName() {
      return fullName;
    }
    return writeName();
  }
  return sayName();
}

在上面的片段中,观察到fullName 这个变量是从writeName() 函数的作用域中被调用的。

因此,从变量的调用到全局范围的作用域链是:

writeName() scope ---> sayName() scope ---> profile() scope ---> global scope

换句话说,从fullName的调用作用域到它的词法作用域(在这个例子中是全局作用域)有四(4)个空格。

注意: 全局作用域是JavaScript作用域链中的最后一个环节。

范围链是如何工作的?

JavaScript的作用域链决定了计算机必须通过一个又一个的地方来找到被调用的特定变量的词法范围(起源)的层次。

例如,考虑下面的代码:

// Define a variable in the global scope:
const fullName = "Oluwatobi Sofela";

// Define nested functions:
function profile() {
  function sayName() {
    function writeName() {
      return fullName;
    }
    return writeName();
  }
  return sayName();
}

在上面的片段中,每当profile() 函数被调用时,计算机将首先调用sayName() 函数(这是profile() 函数中的唯一代码)。

其次,计算机将调用writeName() 函数(它是sayName() 函数中的唯一代码)。

此时,由于writeName() 中的代码指示计算机调用并返回fullName 变量的内容,计算机将调用fullName 。但它不会直接到全局范围去调用fullName

相反,计算机必须一步一步地通过作用域链来寻找fullName词法范围

因此,下面是计算机必须采取的顺序步骤,以找到fullName 的词法范围:

  1. 首先,计算机将检查fullName 是否在writeName() 函数中被局部定义。但它会发现那里没有fullName 的定义,所以它将移到下一个范围继续寻找。
  2. 其次,计算机将在sayName() (作用域链的下一个空间)中搜索fullName 的定义。尽管如此,它还是没有在那里找到它,所以它顺着梯子爬到下一个范围。
  3. 第三,计算机将在profile() 函数中搜索fullName'的定义。然而,那里仍然没有找到fullName 。所以计算机继续前进,在范围链的下一个区域寻找fullName'的词法范围。
  4. 第四,计算机转到全局作用域(在profile() 之后的作用域)。幸运的是,它在那里找到了fullName的定义!因此,它得到了它的内容("Oluwatobi Sofela" )并将其返回。

练习使用范围的时间 🤸♂️🏋️♀️🏊♀️

考虑一下下面的脚本,计算机将调用三个fullName 变量中的哪一个?

// First fullName variable defined in the global scope:
const fullName = "Oluwatobi Sofela";

// Nested functions containing two more fullName variables:
function profile() {
  const fullName = "Tobi Sho";
  function sayName() {
    const fullName = "Oluwa Sofe";
    function writeName() {
      return fullName;
    }
    return writeName();
  }
  return sayName();
}

计算机会调用第一个、第二个还是第三个fullName 变量?

注意: 如果你自己尝试这个练习,你会从这个教程中受益更多。

如果你被卡住了,不要灰心。相反,回顾一下本课,再试一次。

一旦你尽力而为(如果你不这样做,你只会欺骗自己!),请继续看下面的正确答案。

你答对了吗?

在上述脚本中出现的三个fullName定义 中,计算机将调用并返回在sayName() 函数中定义的那个。

sayName() fullName 变量将被调用,因为 是计算机首先找到 定义的范围。sayName() fullName

因此,当profile() 被调用时,返回值将是"Oluwa Sofe"

有些事情要记住

  • 假设计算机没有在任何作用域中找到fullName 的定义。在这种情况下,计算机将返回Uncaught ReferenceError: fullName is not defined
  • 全局作用域总是任何JavaScript作用域链的最后一个作用域。换句话说,全局作用域是所有搜索的终点。
  • 一个内部(子)作用域可以访问它的父(外)作用域,但一个外作用域不能访问它的子作用域。
    例如,在上面的片段中,writeName() 可以访问其任何一个父作用域(sayName(),profile(), 或全局作用域)内的代码。
    然而,无论是sayName(),profile(), 还是global scope 都不能访问writeName() 的任何代码。

到目前为止对作用域的快速回顾

JavaScript的范围都是关于空间的。

所以下次你的伙伴叫你去他们的私人作用域时,请记住他们是在邀请你去他们的私人空间😜!

当你到了那里,一定要问他们最好的词法游戏...

但是,我听到你问,词汇的意思是什么?让我们在下面找出答案。

词汇学是什么意思?

词汇学指的是事物的定义。

任何与创建单词、表达式或变量有关的东西都被称为词法

例如,拼字游戏就是一种词汇活动,因为它与创造词汇有关。

另外,工作与语言学(研究语言)有关的人也是以词汇为职业。

注: 字典的另一个名称是词库。换句话说,词汇表是一本列出和定义单词的字典。

所以,现在我们知道了范围和词表的含义,我们可以谈谈词表范围。

什么是JavaScript中的词法范围?

词汇范围是指一个表达式的定义 区域。

换句话说,一个项目的词法范围是该项目被创建的地方。

注意

  • 词法范围的另一个名称是静态范围
  • 一个项目被调用(或叫)的地方不一定是该项目的词法范围。相反,一个项目的定义空间就是它的词法范围

词法范围的例子

考虑一下下面的代码:

// Define a variable in the global scope:
const myName = "Oluwatobi";

// Call myName variable from a function:
function getName() {
  return myName;
}

在上面的片段中,注意到我们在全局范围内定义了 myName 变量,并在getName() 函数中调用它。

问题是:这两个空格中哪一个是myName的词法范围?是全局范围还是getName() 函数的局部范围?

答案:全局范围。记住,词法范围意味着定义空间--而 不是调用空间。因此,myName的词法范围是全局范围,因为我们在全局环境中定义了myName

词法范围的另一个例子

function getName() {
  const myName = "Oluwatobi";
  return myName;
}

问题: myName'的词法范围是什么?

答案:在哪里注意我们在getName() 中创建并调用了myName 。因此,myName的词法范围是getName()的本地环境,因为getName()myName的定义空间。

词法范围是如何工作的?

一个JavaScript表达式的定义环境决定了允许访问它的代码

换句话说,只有在一个项目的词法范围内的代码才能访问它。

例如,考虑下面的代码:

// Define a function:
function showLastName() {
  const lastName = "Sofela";
  return lastName;
}

// Define another function:
function displayFullName() {
  const fullName = "Oluwatobi " + lastName;
  return fullName;
}

// Invoke displayFullName():
console.log(displayFullName());

// The invocation above will return:
Uncaught ReferenceError: lastName is not defined

请注意,在上面的片段中调用displayFullName() ,返回了一个Uncaught ReferenceError 。之所以返回错误,是因为只有项目的词法范围内的代码才能访问该项目。

因此,displayFullName() 函数和它的内部代码都不能访问lastName 变量,因为lastName 被定义在不同的范围内。

换句话说,lastName'的词法范围与displayFullName()'的词法范围不同。

lastName showLastName() 是其定义空间,而 的词法范围是全局环境。displayFullName()

现在,考虑下面的其他代码:

function showLastName() {
  const lastName = "Sofela";
  return lastName;
}

// Define another function:
function displayFullName() {
  const fullName = "Oluwatobi " + showLastName();
  return fullName;
}

// Invoke displayFullName():
console.log(displayFullName());

// The invocation above will return:
"Oluwatobi Sofela"

在上面的片段中,displayFullName() 成功地返回了"Oluwatobi Sofela" ,因为displayFullName()showLastName() 是在同一个词法范围内。

换句话说,displayFullName() 可以调用showLastName() ,因为这两个函数都定义在全局范围内。

注意

  • 在上面的例子2中,displayFullName() 并没有获得对showLastName()'slastName 变量的访问。
    相反,displayFullName() 调用了showLastName() - 然后返回其lastName 变量的内容。
  • 词法范围的另一个选择是动态范围--但它在编程中很少被使用。只有少数语言,比如bash,使用动态范围。

包裹它

任何时候你听到词法,就想到定义。

所以,汽车、变量、电话、函数或泳装的词法范围是指其定义区域。

概述

这篇文章讨论了词法范围在JavaScript中的含义。我们还研究了为什么它是一个重要的编程概念。

谢谢你的阅读!