JS作用域链详解

JS作用域链详解

JavaScript采用词法作用域,也就是变量的作用域在定义时就已经确定了。而在JavaScript中,作用域可以形成一个链式结构,这被称为作用域链。在这个链结构中,每一个函数都有自己的作用域,如果一个变量在当前作用域中未定义,则会沿着作用域链向上查找,直到查找到该变量为止,或者到达全局作用域。

作用域链的构成

JavaScript中的作用域链是由一个个执行上下文(Execution Context)构成的。而每一个执行上下文又分为三个部分:变量对象(Variable Object),作用域链(Scope Chain)和this对象。

变量对象是当前执行上下文中的变量、函数声明和形参的存储空间,作用域链是当前执行上下文的父环境。变量对象通过作用域链查找某个变量的值。this对象是函数调用时的当前对象。

作用域链形成的过程:在函数执行的时候,会首先创建一个新的执行上下文,然后将这个上下文的变量对象加入到作用域链的最顶端,接着将父级执行上下文的作用域链加入到自身作用域链的末尾,最后形成的作用域链便是可以被访问的所有变量、函数或对象的作用域。

作用域链的查找

当JavaScript引擎在执行代码时遇到一个变量,它会先在当前作用域中查找该变量是否被定义,如果没找到,那么就会沿着作用域链向上查找,直到找到为止。如果一直找到全局部还没找到,则会抛出ReferenceError的异常。如果找到了,就直接使用这个变量,而不需要再次创建这个变量。

具体来说,当需要查找某个变量时,JavaScript引擎会先在当前执行上下文中的变量对象中查找该变量,在该对象中找到该变量就不需要再进行后续的查找了;否则就把当前执行上下文的作用域链中的父级执行上下文的变量对象加入到自身作用域链的末尾,然后继续执行上述查找逻辑,直到查找到该变量,或者直到作用域链的末尾。

示例说明

下面通过两个例子进一步演示作用域链的应用:

例子1:全局环境和函数环境之间的作用域链

var a = 1;
function foo() {
  var b = 2;
  function bar() {
    var c = 3;
    console.log(a, b, c);
  }
  bar();
}
foo(); // 输出:1 2 3

在该例中,当执行foo函数时,会首先创建一个名为foo的执行上下文,并将该上下文的变量对象加入到作用域链的顶端。此时作用域链为:foo变量对象 => 全局变量对象

当执行到bar函数时,JavaScript会同样创建一个名为bar的执行上下文,并将该上下文的变量对象加入到作用域链的顶端。此时作用域链为:bar变量对象 => foo变量对象 => 全局变量对象

因此,当bar函数执行时,会先在该函数作用域中查找c变量,然后在上层的foo作用域中查找b变量,最后在全局作用域中查找a变量。

例子2:作用域链的动态性

var a = 10;
function foo() {
  console.log(a);
  var b = 20;
  function bar() {
    console.log(a, b, c);
  }
  bar();
}
foo();

在该例中,当执行foo()函数时,JavaScript引擎会首先查找当前作用域下的变量对象,由于当前作用域下不存在变量a,因此JavaScript引擎会沿着作用域链向上查找,找到它的父级作用域,也就是全局作用域,在全局作用域中找到了变量a的值,因此执行完console.log(a)语句后,即可输出变量a的值为10

接着JavaScript引擎执行到bar()函数时,会创建一个新的执行上下文,并将该上下文的变量对象加入到作用域链的顶端。此时作用域链为:bar变量对象 => foo变量对象 => 全局变量对象

由于变量c未定义,在当前执行上下文中查找不到变量c,因此JavaScript引擎继续在上一级执行上下文中查找变量c,但上一级执行上下文依然查找不到变量c,更上一级执行上下文也查找不到变量c,最终在全局变量对象中仍然找不到变量c,因此引擎抛出了ReferenceError错误,提示变量c未定义。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:JS作用域链详解 - Python技术站

(0)
上一篇 2023年6月10日
下一篇 2023年6月10日

相关文章

  • 原生 JS Ajax,GET和POST 请求实例代码

    下面是关于“原生 JS Ajax,GET 和 POST 请求实例代码”的完整攻略。 1. 前置知识 在学习原生 JS Ajax,GET 和 POST 请求之前,你需要掌握以下知识: 前端基础知识,如 HTML,CSS,JavaScript。 HTTP 协议基本概念和请求方式(GET 和 POST)的理解。 2. Ajax 请求 Ajax 是一种在后台与服务器…

    JavaScript 2023年6月11日
    00
  • JavaScript字符串插入、删除、替换函数使用示例

    关于JavaScript字符串插入、删除和替换函数的使用,以下是完整攻略: 字符串插入 在字符串中插入新的字符或文本是一个常见的需求。在JavaScript中实现这个功能有多种方法,其中最简单的方法是使用字符串的concat()函数。 concat()函数可以将字符串连接到另一个字符串上。例如,我们可以将“Goodman”插入到“hello”字符串之后。示例…

    JavaScript 2023年5月28日
    00
  • 一起来学习JavaScript的BOM操作

    一起来学习JavaScript的BOM操作 什么是BOM BOM(浏览器对象模型)是指浏览器提供的一组API,用于控制浏览器窗口或标签页。我们可以使用BOM来操作浏览器窗口的大小、位置、前进后退等行为,以及判断当前浏览器类型、浏览器版本和语言。 BOM对象 BOM主要由4个对象组成: window对象:代表整个浏览器窗口,是BOM对象的最外层对象。 navi…

    JavaScript 2023年6月11日
    00
  • 基于javascript如何传递特殊字符

    要在JavaScript中传递特殊字符,需要使用转义字符来表示这些字符。常见的特殊字符包括单引号、双引号、反斜杠、换行符、制表符等。以下是关于如何在JavaScript中传递特殊字符的步骤和示例代码: 使用反斜杠 在JavaScript中,使用反斜杠来转义特殊字符。例如,要在字符串中表示单引号,可以使用反斜杠对其进行转义。 示例代码: let str = ‘…

    JavaScript 2023年5月19日
    00
  • 学习JavaScript设计模式(链式调用)

    学习 JavaScript 设计模式是提高前端开发技能的重要途径之一。链式调用是其中较为常见的一种模式,它在 jQuery 等插件库中得到广泛应用。下面是学习 JavaScript 设计模式(链式调用)的完整攻略一: 1. 什么是链式调用 链式调用是一种 JavaScript 设计模式,它允许在单行代码中执行多个操作。在链式调用的过程中,一个对象的方法会返回…

    JavaScript 2023年6月10日
    00
  • 浅析Javascript使用include/require

    浅析 Javascript 使用 include / require Javascript 不同于其他编程语言存在预编译及模板引入机制,因此导致在项目开发过程中可能出现一个 JS 文件需要导入其他 JS 文件中的函数或变量的情况,此时就需要使用 include 或 require 进行模块引入操作。 include 与 require include 与 r…

    JavaScript 2023年5月27日
    00
  • JavaScript数组、json对象、eval()函数用法实例分析

    接下来我将详细讲解“JavaScript数组、JSON对象、eval()函数用法实例分析”的完整攻略。 一、JavaScript数组 1.1 定义数组 JavaScript数组是一种数据类型,用于存储多个数据,可以是数值、字符串、对象等。定义一个数组可以使用以下语法: var myArray = new Array(); // 使用 new 操作符创建一个空…

    JavaScript 2023年5月27日
    00
  • 在JavaScript中实现链式调用的实现

    实现链式调用,可以让代码更加简洁、易读、易维护,主要分为两个步骤:实现方法的返回值为对象本身,实现链式调用调用下一个方法。 实现方法的返回值为对象本身 在JavaScript中,方法可以返回对 对象实例本身 的引用,这样就可以在同一个对象上多次调用不同的方法了。在方法的末尾,使用 return this 将当前对象实例返回即可。 下面是一个简单示例: con…

    JavaScript 2023年6月11日
    00
合作推广
合作推广
分享本页
返回顶部