JavaScript作用域从局部到全局介绍

本篇文章和大家了解一下JavaScript作用域从局部到全局介绍。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。

JavaScript作用域深度剖析:从局部到全局一网打尽

1.1 编译原理

  • JavaScript 事实上是一门编译语言。

在传统编译语言中,一段源代码执行前会经历三个步骤:

分词/词法分析(Tokenizing/Lexing)

var a = 2;
// 分解后:
var、a、=、2、;
// 空格是否会被当做词法单元,取决于空格在这门语言中是否具有意义。
  • 期间经过两个过程:分词(tokenizing)和词法分析(Lexing) 、两者的主要差别在于词法单元的识别是通过有状态还是无状态的方式进行的。

解析/语法分析(Parsing)

  • 这个过程就是将词法单元流(数组)转换为一个由元素逐级嵌套组成的代表了程序语法结构的树,这个树被称为"抽象语法树"。(Abstract Syntax Tree, AST)。

代码生成

  • 将 AST 转换为可执行代码的过程被称为代码生成。也就是说有某种方法将 var a = 2; 的 AST 转换为一组机器指令,用来创建一个叫做 a 的变量(包含分配内存等),将一个值储存于 a 中。

  • 比起其他编译过程只有这三个步骤的语言的编译器,JavaScript 引擎要复杂得多,在语法分析和代码生成阶段有着特定的步骤来对比运行性能进行优化,包括对冗余元素进行优化等。

  • 简单来说,任何 JavaScript 代码片段在执行前都要进行编译(通常就在执行前)

1.2 理解作用域

1.2.1 演员表
  • 引擎:从头到尾负责整个 JavaScript 程序的编译及执行过程。

  • 编译器:引擎的好朋友之一,负责语法分析及代码生成等脏活累活。

  • 作用域:引擎的另一个好朋友,负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实行一套严格的规则,确定当前执行的代码对这些标识符的访问权限。

1.2.2 对话
  • var a = 2; 这段代码是一句声明。但会经过编译器和引擎的处理来进行。

  • S: 变量的赋值操作会执行两个动作,首先编译器会在当前作用域中声明一个变量(如果之前没有声明过),然后在运行时引擎会在作用域中查找该便令,如果能够找到就会对它进行赋值。

1.2.3 编译器有话说

编译器在编译过程中的第二步中生成了代码,引擎执行它时,会通过查找变量 a 来判断他是否已声明过。查找的过程由作用域进行协助,但是引擎执行怎样的查找会影响最终的查找结果。

引擎常使用的查询类型为:LHS和RHS

LHS: 赋值操作的目标是谁

RHS: 谁是赋值操作的源头

1.2.5
function foo(a) {
    var b = a;
    return a + b;
}
var c = foo(2);
// 对话:
1. 声明 var c
2. 对 c 进行 LHS
3. 对 foo(2) 进行 RHS
4. function foo(a) 期间会进行 a = 2, 对 a 进行 LHS
5. 声明 var b
6. 对 b 进行 LHS
7. 对 a 进行 RHS
8. return a + b; 分别对 a、b 进行 RHS
// 答案:
1. 所有的 LHS(一共有3处)
    1. c =..;
    2. a = 2(隐士变量分配)
    3. b = ..
2. 所有的 RHS (一共有4处)
    1. foo(2..
    2. = a;
    3. a..
    4. .. b

1.3 作用域嵌套

作用域是根据名称查找变量的一套规则。

当一个块或函数嵌套在另一个块或函数中时,就会发生作用域的嵌套。因此在当前作用域中无法找到某个变量时,引擎就会在外层作用域中继续查找,直到找到该变量,或抵达最外层的作用域(也就是全局作用域)为止。

// 非严格模式下
function foo(a) {
console.log(a + b);
}
var a = 2;
foo(2); // 4
// 严格模式下:
function foo(a) {
console.log(a + b);
}
var a = 2;
foo(2); // 4

遍历嵌套作用域链的规则:引擎会从当前的执行作用域中开始查找变量,如果找不到就会向上一级中继续查找。当抵达最外层的全局作用域时,无论找到还是没找到,查找的过程都会停止。

例子:

JavaScript作用域从局部到全局介绍

  • 整个建筑代表程序中的嵌套作用域链,第一层楼代表当前的执行作用域,也就是你所处的位置。建筑的顶层代表全局作用域。

  • 引擎查找的方式:LHS 和 RHS 引用会先在当前楼层中进行查找,如果没找到,就会坐电梯前往上一层楼楼,如果还是没找到就会继续上下,以此类推。一旦达到了顶层(全局作用域), 可能找到你了你所需的变量,也可能没找到,但无论如何查找过程都会停止。

1.4 异常

为什么区分 LHS 与 RHS 是一种重要的事?

因为在变量还未声明(在任何作用域中都无法找到该变量)的情况下,引擎的这两种查询行为是不一样的。

// 非严格模式下:
function foo(a) {
console.log(a + b);
b = a;
}
foo(2); // 4
// 严格模式下:
'use strict';
function foo(a) {
console.log(a + b);
b = a;
}
foo(2); // ReferenceError: b is not defined

上述代码引擎行为:

  • 非严格模式下:

第一次对 b(.. + b) 进行 RHS 查询时未找到该变量,也就是说,这是一个"未声明" 的变量,因为在任何相关的作用域都无法找到它。

第二次对 b(b = ..) 进行 LHS 查询时,如果在顶层(全局作用域)中也没找到该变量,就会在全局作用域中隐式地创建一个该名称的变量,并将其返回给引擎。

......

  • 严格模式下:

第一次对 b(.. + b) 进行 RHS 查询时未找到该变量,也就是说,这是一个"未声明" 的变量,因为在任何相关的作用域都无法找到它,直接抛出 'ReferenceError'。

......

  • 非严格模式下引擎查找规则

    当引擎执行 RHS 查询在所有嵌套的作用域中找不到所需的变量,引擎就会抛出 ReferenceError 异常。

    当引擎执行 LHS 查询时,如果在顶层作用域中也无法找到该变量,全局作用域就会创建一个该名称的变量,并将其返回给引擎(非严格模式下)。

  • 严格模式下引擎查找规则

    ES5 引入了 "严格模式"(use strict),在行为上有很多不同,其中一个不同的行为就是严格模式下禁止自动或隐式地创建全局变量。因此在严格模式中引擎执行 LHS 查询失败时,并不会创建一个全局变量,而是直接抛出一个 ReferenceError

    如果 RHS 找到了一个变量,但尝试对这个变量进行一些不合理的操作时,比如对一个非函数类型的值进行函数调用,或者引用 null 或 undefined 类型的之中属性,那引擎则会抛出另外一种类型的异常 TypeError。

  • ReferenceError 同作用域判断失败相关,而 TypeError 代表作用域判别成功了,但对结果的操作是非法或不合理的。

1.5 小结

作用域是根据名称查找变量的一套规则。

引擎常使用的查询类型为:LHS 和 RHS

  • LHS: 赋值操作的目标是谁

  • = 操作符在调用函数时的形参会导致关联作用的赋值操作。也就是说 foo (a, b, c...), 都会有 a = xxx, b = xxx, c = xxx ...... 的行为。

  • RHS: 谁是赋值操作的源头

非严格模式下引擎查找规则

当引擎执行 RHS 查询在所有嵌套的作用域中找不到所需的变量,引擎就会抛出 ReferenceError 异常。

当引擎执行 LHS 查询时,如果在顶层作用域中也无法找到该变量,全局作用域就会创建一个该名称的变量,并将其返回给引擎(非严格模式下)。

严格模式下引擎查找规则

ES5 引入了 "严格模式"(use strict),在行为上有很多不同,其中一个不同的行为就是严格模式下禁止自动或隐式地创建全局变量。因此在严格模式中引擎执行 LHS 查询失败时,并不会创建一个全局变量,而是直接抛出一个 ReferenceError

如果 RHS 找到了一个变量,但尝试对这个变量进行一些不合理的操作时,比如对一个非函数类型的值进行函数调用,或者引用 null 或 undefined 类型的之中属性,那引擎则会抛出另外一种类型的异常 TypeError。

  • ReferenceError 同作用域判断失败相关,而 TypeError 代表作用域判别成功了,但对结果的操作是非法或不合理的。

特殊字符描述:

  • 问题标注 Q:(question)

  • 答案标注 R:(result)

  • 注意事项标准:A:(attention matters)

  • 详情描述标注:D:(detail info)

  • 总结标注:S:(summary)

  • 分析标注:Ana:(analysis)

  • 提示标注:T:(tips)

以上就是JavaScript作用域从局部到全局介绍的简略介绍,当然详细使用上面的不同还得要大家自己使用过才领会。如果想了解更多,欢迎关注编程之家行业资讯频道哦!

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


阅读本文之前,分享大家一张图片,看图会发现JavaScript开发需求最高,占比达到42.84%,因此掌握JavaScript语言好工作就不愁啦,工欲善其事必先利其器,那么选择IDE来开发是至关重要的,本文指出常用的几款JavaScript IDE,分析其优缺点,如有不完善的请大家补充
Promises是一种关于异步编程的规范,目的是将异步处理对象和处理规则进行规范化,为异步编程提供统一接口。本文简要的介绍了Promises的基础知识,希望我们我们能够更好的使用Promises,更轻松的编写代码。
引子 Patrick Catanzariti 是一名Web开发工程师,最近他在 sitepoint 发表了《JavaScript Beyond the Web in 2014》,介绍了JavaScript在物联网中的应用,非常有意思。做为JavaScript的爱好者和从业者,我在这里把它翻译了,以飨
小编吐血整理加上翻译,太辛苦了~求赞! 本文主要总结了JavaScript 常用功能总结,如一些常用的JS 对象,基本数据结构,功能函数等,还有一些常用的设计模式。 目录: 众所周知,JavaScript是动态的面向对象的编程语言,能够实现以下效果: 1. 丰富Web 网页功能 2. 丰富Web界面
微软于今日(2015年12月10日)宣布即将开源Chakra核心控件,并改名为“ChakraCore”,该控件包含所有Edge JavaScript 引擎的所有核心功能。ChakraCore 将于下月发布在GitHub中。 Chakra提供了顶级的JavaScript处理功能,并具有非常强大的性能优
通过统计数据库中的1000多个项目,我们发现在 JavaScript 中最常出现的错误有10个。本文会向大家介绍这些错误发生的原因以及如何防止。
TypeScript 和 JavaScript 是目前项目开发中较为流行的两种脚本语言,我们已经熟知 TypeScript 是 JavaScript 的一个超集,但是 TypeScript 与 JavaScript 之间又有什么样的区别呢?在选择开发语言时,又该如何抉择呢?
本文是2017年 JavaScript 框架回顾系列的最后的一篇文章,主要介绍 JavaScript 的后端框架情况。
本文来源于多年的 JavaScript 编码技术经验,适合所有正在使用 JavaScript 编程的开发人员阅读。本文的目的在于帮助大家更加熟练的运用 JavaScript 语言来进行开发工作。
对于前端开发人员来说,如果能够掌握交互式网页中的数据可视化技术,则是一项很棒的技能。当然,通过一些 JavaScript 的图表库也会使前端的数据可视化变得更加容易。
几乎每隔一个星期,就有一个新的 JavaScript 库席卷网络社区!Web 社区日益活跃、多样,并在多个领域快速成长。想要研究每一个重要的 JavaScript 框架和库,是个不可能完成的任务。接下来,我会分享一些前端开发的最著名和最有影响力的框架和库。下面,就让我们一起来看看,顶级的 JavaS
AngularJ.js 由google开发,2009年首次发布 很流行的前端框架 使用Angular.js创建第一个UI,成本很低 对于团队来说,AngularJ.js有许多很棒的工具可用 很适合创建一个快速、混合型复杂的解决方案 比起React,更合适于创建小型企业级应用 由Google负责维护基
Javascript框架(以下简称框架)也被称为Javascript库,是一组包含丰富功能和函数的JavaScript代码集,能够帮助开发者快速完成Web设计和开发工作。随着Web社区的越发活跃,新的框架也层出不穷,目前流行的有:Angular、React、Vue.js和Knockout等。 面对如
对于 JavaScript 社区来说,npm 的主要功能之一就是帮助开发者发掘所需的 npm Registry 中的库和框架。npm 强大的搜索功能能够帮助找到一组相关的软件包,同时其内置的的文档和使用统计信息,可以帮助开发者决定使用哪一种软件包。
前言 SpreadJS作为一款性能出众的纯前端电子表格控件,自2015年发布以来,已经被广泛应用于各领域“在线Excel”数据管理项目中。NPM,作为管理Node.js库最有力的手段,解决了很多NodeJS代码部署的问题。 如今,为让您更方便的使用产品和更好地管理项目中的SpreadJS代码,我们已
前一篇文章中,我们介绍了2017年 JavaScript 框架的整体情况。我们也了解到在众多的前端框架中,目前最为庞大又在快速增长的当属React了,本文就来重点介绍React的生态系统。
ES2017标准已经于2017年6月份正式定稿了,并广泛支持最新的特性:异步函数。如果你曾经被异步JavaScript的逻辑困扰,这么新函数正是为你设计的。
本文将会讨论10个优秀的支持JavaScript,HTML5和CSS开发,并且可以使用Markdown进行文档编写的文本编辑器。
随着现在的编程语言功能越来越成熟、复杂,内存管理也容易被大家忽略。本文将会讨论JavaScript中的内存泄漏以及如何处理,方便大家在使用JavaScript编码时,更好的应对内存泄漏带来的问题。
JavaScript 作为当前最为常见的直译式脚本语言,已经广泛应用于 Web 应用开发中。为了提高Web应用的性能,从 JavaScript 的性能优化方向入手,会是一个很好的选择。本文从加载、上下文、解析、编译、执行和捆绑等多个方面来讲解 JavaScript 的性能优化技巧,以便让更多的前端开