返回顶部
首页 > 资讯 > 前端开发 > JavaScript >JavaScript作用域与作用域链优化方式
  • 870
分享到

JavaScript作用域与作用域链优化方式

2024-04-02 19:04:59 870人浏览 安东尼
摘要

目录前言内容作用域作用域的嵌套作用域的一些实现细节作用域链相关优化总结前言 作用域和作用域链是所有javascript开发人员每天都要接触和应用的内容。不管是面试中的作用域链的面试考

前言

作用域和作用域链是所有javascript开发人员每天都要接触和应用的内容。不管是面试中的作用域链的面试考察,还是日常代码研发中变量与作用域链的构建,它的身影几乎无处不在。它就像一顶优秀厨师的厨师帽,只要我们走进厨房,我们就要将它整理好,套在头上。没有它整洁干净的戴在头上,你就不是一名好的JavaScript工程师。

其实,作为一名前端工程师,我也曾经疑惑过:基本上所有的计算机语言都具有作用域的概念,但是为何JavaScript开发人员总是对作用域这个概念执着不已?直到,我多次在编写代码过程中遇到涉及到作用域的问题后,我才渐渐了解这个问题并去仔细研究。

而这篇文章,就是想要和大家聊聊有关JavaScript作用域以及作用域链的那些事情,以及针对它们的一些我们在代码中优化小技巧。

内容

对于几乎所有的编程语言来说,最基本的功能之一,就是储存变量当中的值并且能在之后对这个值进行访问和修改。这种能力的引入,是程序的状态存在的基础。但是,能力的引入需要我们解决几个问题,例如:变量存储在哪里?以何种形式存储?需要读取和修改变量的时候,以什么方式获取到这个变量?

很明显,为了解决这些问题,我们需要一套设计良好的规则来存储变量,并且之后可以方便的找到这些变量。与此同时,整套完整规则的设计就会衍生出额外规则概念。而作用域,就是这套规则下衍生出来的概念。

作用域

我们可以把作用域理解为上面讲到的这套规则下的限定范围。作用域的职责是,在这段限定范围中根据这套设计好的规则存储所声明的变量,并且提供修改该变量的支持。在变量的访问权限安全上,作用域还承担着保护当前作用域内的变量不被外部作用域访问的权限保护作用。

通过类比,我们可以把作用域想象成一个气泡。在这个气泡里所声明的变量成员被包含在其中。每个气泡都配备有一位有原则的管家,将所有的成员管理起来,并针对他们声明的位置和要求对它们提供保护。当气泡中代码语句想要访问和修改变量成员时,管家会结合变量成员的要求关联对应访问和修改操作。

随着ECMAScript标准的不断发展和完善,JavaScript目前存在着四种作用域类型:

  • 全局作用域(Global Scope): JavaScript语言环境的最顶级作用域,在语言环境初始化时创建。
  • 模块作用域(Module Scope): 由ECMAScript模块标准(ES Module)引入,在解析ECMAScript模块时创建。
  • 函数作用域(Function Scope): 在函数声明function() {}或者() => {}时创建。
  • 块级作用域(Block Scope): 由ECMAScript2015的变量声明标识符letconst引入,在使用这两者进行变量声明时,根据最近的一对花括号{}创建。


let name = 'Wu';
{
  
  const prefix = Hardy;
  name = prefix + name;
  
}
export function sayMyName(myName) {
  
  if (!myName) {
    
    const noNameAnswer = 'Sorry!';
    console.log(noNameAnswer);
    return;
    
  }
  const WordPrifix = 'Hi! My Name is ';
  const answer = wordPrifix + myName + '.';
  console.log(answer);
  
}

作用域的嵌套

作用域在使用上具有嵌套特征。一个作用域能够在自身内部创建一个新作用域从而形成内部和外部作用域的嵌套关系。

全局作用域作为JavaScript的初始作用域,是所有其他作用域最外层的作用域。另外,每一个ES Module都具有模块自己的顶级作用域(top-level scope),模块中的顶级作用域变量和函数都包含在这个模块顶级作用域中,而模块作用域的外部作用域是全局作用域。而函数作用域和块级作用域则相对比较灵活,可以相互嵌套。

作用域的一些实现细节

在JavaScript中,每一个函数、代码块{...}以及script脚本被运行前,都会有一个相对应的称为词法环境(Lexical Environment) 的内部关联对象被创建。

词法环境由两部分组成:

  • 环境记录(Environment Record):一个存储所有局部变量作为其属性(包括一些执行上下文信息,例如this的值)的对象。
  • 外部词法环境引用(Outer):对外部词法环境的引用,以此关联外部词法环境。

代码执行的过程中,每一个局部变量和局部函数的声明,都会作为一个属性字段被添加到环境记录中,后续对变量和函数的读取则通过对应标识符在环境记录中进行查找。

根据上面的概念,我们可以通过下面的对象结构理解词法环境:

  lexicalEnvironment = {
    environmentRecord: {
      <identifier>: <value>,
      <identifier>: <value>,
    },
    outer: <Reference to the parent lexical environment>,
  }

再来通过下面的代码例子来理解词法环境:


let name = 'Hardy';

function sayName(myName) {
  
  
  console.log(myName);
}
sayName(); // Hardy

我们来分析下上面的代码例子:

根据声明提前的特性,变量name和函数sayName都会在模块的词法环境创建时被添加在环境记录中。但是,由于let的暂时性死区特性,变量name在自身声明和初始化赋值之前处于不可引用和未初始化状态。函数的声明则不同,除了声明提前外还会初始化函数的引用。这就是我们可以在函数执行声明语句前调用函数的原因。另外,函数的词法环境在被创建时,对应函数的参数会被初始化在环境记录中,并且会被赋值上调用函数时的所传值或者函数参数的默认值。

outer引用方面,模块词法环境moduleLexicalEnvironmentouter引用指向JavaScript最外部的全局词法环境globalLexicalEnvironment,而函数词法环境functionLexicalEnvironmentouter引用指向外部的模块词法环境moduleLexicalEnvironment

我们可以看出,词法环境是JavaScript对作用域概念的内部技术实现。它是JavaScript引擎创建一个执行上下文时,创建用来存储变量和函数声明的环境。代码执行过程中,通过它访问到存储在其内部的变量和函数。在代码执行完毕后,执行上下文会从堆栈中被销毁回收,而词法环境也会根据情况的被销毁(如果词法环境被其他外部的词法环境所引用,则不会被销毁回收,例如闭包)。

作用域链

作用域可以嵌套,嵌套在内部的作用域可以访问外部的作用域所声明的变量和函数。通过上面词法环境的介绍,我们大概清楚,作用域的这种嵌套关系是通过词法环境的外部词法环境引用outer来关联实现的。这种词法环境的外部引用的关联关系,构建了一条单向的词法环境的链条。这就是我们常说的作用域链。

本质上,作用域链是JavaScript引擎给所执行代码维护的一条词法环境链条。代码执行中对外部作用域的变量的引用,通过这一条链条进行变量的查找、读取、修改。

代码执行中对某个变量的访问大致如下:

  • 当代码要访问一个变量时,首先会搜索当前内部词法环境。如果搜索成功,就返回对一个变量值或变量引用,结束搜索。如果搜索不到,则通过outer引用继续搜索外部词法环境,以此类推,直到全局词法环境。
  • 如果在任何地方都找不到这个变量,那么在严格模式下就会报错。

根据上面的概念,我们来看看下面的例子:

let phrase = 'Hello';

function sayHello(name) {
  
  console.log(`${phrase}, ${name}!`);
}
sayHello('Hardy'); // Hello, Hardy!

上面例子中,函数sayHello在内部引用了namephrase两个变量,函数被调用的执行时会创建functionLexicalEnvironment > moduleLexicalEnvironment > globalLexicalEnvironment的作用域链。

其中,变量name作为函数参数属于当前函数作用域的局部变量,变量可以直接从当前函数的词法环境functionLexicalEnvironment中查找到并返回相关信息。而变量phrase属于外部作用域中声明的变量,存储在外部的模块词法环境moduleLexicalEnvironment中。函数sayHello引用变量phrase,会首先从在自身函数词法环境functionLexicalEnvironment中进行查找,查找不到后,会沿外部词法环境引用outer找到模块词法环境moduleLexicalEnvironment,并从中继续进行变量的查找,查找到了并返回变量的相关信息。

值得注意的是console.log()是全局内置对象console上的方法,对该方法的调用需要引用console。这个变量的引用会沿作用域链一直查找到全局词法环境globalLexicalEnvironment中,从中查找到并返回相关变量信息。

变量标识符解析和引用的过程就是沿作用域链迭代查找变量是否在作用域链节点中并返回变量相关信息的过程。

相关优化

综合上面的标识符的解析过程和作用域以及作用域链的关系,我们可以了解到,变量标识符解析的性能是和变量标识符所处在作用域链中的位置是息息相关的。变量标识符所出的作用域节点越靠近整个作用域链的前端,则需要沿作用域链迭代查找的次数就越少,变量标识符解析的速度就会越快,性能就越好。

这种标识符解析性能的规律,让我们可以得出以下使用变量的优化点:

  • 对于频繁引用的外部作用域的变量,可以根据情况在当前作用域内声明赋值为局部变量后使用。
  • 减少作用域增强with语句的使用。

外部作用域变量标识符的多次引用,会造成执行过程中的标识符解析沿作用域链查找的频繁执行,这种查找在第一次解析引用时是必须的,但是后续解析引用却是重复的。将外部作用域变量通过在当前作用域内声明赋值为局部变量,可以优化后续查找的需要经过的作用域链节点个数,得到一定的性能提升。

with语句可以在当前作用域链前端临时添加一个词法环境,从而在位置构建和使用新的作用域链。但是这方式问题也很显而易见:作用域链被加长了,除了被添加到前端的词法环境中的存储的变量外,其他变量的标识符解析性能都会变差。因此,我们应该减少with语句的使用。

总结

随着JavaScript语言的发展,语言中的作用域的种类也变得丰富起来,不再局限于函数作用域作为最小变量声明范围来使用,而是可以基于更小范围的跨级作用域来管理我们的变量引用范围。变量的管理变得更加的灵活、安全。

作用域链是作用域链嵌套的结构产物,所有变量标识符的解析和引用会沿着作用域链进行查找。而词法环境,是JavaScript对于作用域的内部技术实现。深入了解词法环境后,也让我们更清楚代码在解析变量标识符时的内部执行过程。也根据这个过程,我们大概总结出了两点关于作用域和变量使用的性能优化点。

作用域的使用作为每一位JavaScript开发人员的必修课,了解得深入才能在使用它的时候不再迷茫。它就像空气,存在于JavaScript的许多地方,值得我们去好好了解。

到此这篇关于JavaScript作用域与作用域链优化方式的文章就介绍到这了,更多相关js 作用域链优化内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: JavaScript作用域与作用域链优化方式

本文链接: https://lsjlt.com/news/164938.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • JavaScript作用域与作用域链优化方式
    目录前言内容作用域作用域的嵌套作用域的一些实现细节作用域链相关优化总结前言 作用域和作用域链是所有JavaScript开发人员每天都要接触和应用的内容。不管是面试中的作用域链的面试考...
    99+
    2024-04-02
  • JavaScript作用域与作用域链使用重点讲解
    作用域和作用域链方面的知识是JS的重点,去面试十个有八个都会问你这块的知识,所以说这块是特别特别的重要,下面我们好好理解一下作用域和作用域链到底是个什么: 先上一段代码: var a...
    99+
    2022-11-13
    JS作用域与作用域链 JS作用域 JS作用域链
  • javascript作用域和作用域链详解
    目录一、javascript的作用域1、全局作用域2、局部作用域二、javascript的作用域链三、作用域链和优化四、改变作用域链1、with语法改变作用域链2、catch语法总结...
    99+
    2024-04-02
  • 什么是作用域与作用域链
    本篇内容主要讲解“什么是作用域与作用域链”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“什么是作用域与作用域链”吧!作用域如果说执行上下文是代码的执行环境,那么作...
    99+
    2024-04-02
  • 怎么使用作用域与作用域链
    这篇文章主要讲解了“怎么使用作用域与作用域链”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么使用作用域与作用域链”吧!一、作用域如果说执行上下文是代码的执...
    99+
    2024-04-02
  • Javascript的作用域和作用域链是什么
    这篇文章主要介绍Javascript的作用域和作用域链是什么,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!Javascript 的作用域和作用域链作用域: 作用域是定义变量...
    99+
    2024-04-02
  • 如何分析javascript作用域和作用域链
    如何分析javascript作用域和作用域链,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。一、javascript的作用域1、全局作用域1、最外层函数和最外层函...
    99+
    2023-06-28
  • JavaScript作用域和作用域链的示例分析
    这篇文章给大家分享的是有关JavaScript作用域和作用域链的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。作用域(Scope)1. 什么是作用域作用域是在运行时代码中...
    99+
    2024-04-02
  • Javascript的作用域、作用域链以及闭包详解
    一、javascript中的作用域 ①全局变量-函数体外部进行声明 ②局部变量-函数体内部进行声明 1)函数级作用域 javascript语言中局部变量不同于C#、Java等高级语言...
    99+
    2024-04-02
  • JavaScript作用域链是什么
    本篇内容介绍了“JavaScript作用域链是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!作用域1....
    99+
    2024-04-02
  • Javascript之作用域、作用域链、闭包的示例分析
    这篇文章主要介绍Javascript之作用域、作用域链、闭包的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!什么是作用域?作用域是一种规则,在代码编译阶段就确定了,规定了变量...
    99+
    2024-04-02
  • js作用域及作用域链工作引擎
    目录前言一、作用域(scope)1.作用域的分类2.函数体作用域3.块级作用域二、预编译三、作用域链前言 我们需要先知道的是引擎,引擎的工作简单粗暴,就是负责javascript从头...
    99+
    2024-04-02
  • JavaScript的作用域链有什么用
    JavaScript的作用域链有什么用,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。JavaScript的作用域链这是一个非常...
    99+
    2024-04-02
  • javascript作用于作用域链的示例分析
    这篇文章主要介绍javascript作用于作用域链的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!一、JavaScript作用域任何程序设计语言都有作用域的概念,简单的说,作...
    99+
    2024-04-02
  • JavaScript详细解析之作用域链
    以上就是JavaScript详细解析之作用域链的详细内容,更多请关注编程网其它相关文章!...
    99+
    2022-11-22
    JavaScript 前端
  • JavaScript深入理解作用域链与闭包详情
    目录深入作用域链与闭包作用域链[[Environment]]完善环境记录闭包函数实例什么是闭包变量绑定同一个闭包总结深入作用域链与闭包 为什么要把作用域链和闭包放在一起讲呢,它们有什...
    99+
    2024-04-02
  • js作用域及作用域链工作引擎怎么应用
    本文小编为大家详细介绍“js作用域及作用域链工作引擎怎么应用”,内容详细,步骤清晰,细节处理妥当,希望这篇“js作用域及作用域链工作引擎怎么应用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。一、作用域(scope...
    99+
    2023-07-02
  • 图解JavaScript作用域链底层原理
    目录前言 作用域 1.什么是作用域 2.[[Scopes]]属性 3.作用域链 4.图解查找变量原理 总结 前言 在学习JavaScript时大家一定都知道,外部空间不能访问内部变...
    99+
    2024-04-02
  • JS作用域和作用域链的区别是什么
    本篇内容介绍了“JS作用域和作用域链的区别是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!作用域(Sc...
    99+
    2024-04-02
  • JavaScript 中的作用域与闭包
    目录一、JavaScript 是一门编译语言1.1 传统编译语言的编译步骤1.2 JavaScript 与传统编译语言的区别二、作用域(Scope)2.1 LHS查询 和 RHS查询...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作