JavaScript 做用域不彻底指北

什么是做用域

对于几乎全部编程语言,最基本的功能之一就是可以存储变量的值,而且能在以后对这个值进行访问和修改。这样就会带来几个问题,这些变量存储在哪里?程序在须要的时候又是如何找到它们的?要解决这些问题,就须要引入一套规则来存储变量和访问变量,这套规则就是做用域。javascript

理解做用域

在刚开始接触 JavaScript 这门语言时,确定会常常接触到 JavaScript 是动态语言, 是解释执行的,但事实上 JavaScript 是一门编译语言。只不过和 Java、C# 这些传统意义上的编译语言不一样,JavaScript 的编译过程不是发生在构建以前的。大部分状况下,JavaScript 的编译过程发生在代码执行前的很短期内。也就是说 JavaScript 代码在执行前都要进行编译。java

为了更好地理解做用域,咱们须要明确下面几个概念编程

  • 引擎

从头至尾负责整个 JavaScript 程序的编译及执行过程微信

  • 编译器

负责语法分析及代码生成等脏活累活编程语言

  • 做用域

负责收集并维护由全部声明的标识符(变量) 组成的一系列查询, 并实施一套很是严格的规则, 肯定当前执行的代码对这些标识符的访问权限。函数

下面咱们从引擎、编译器和做用域的角度,分析 var a = 2 这条声明语句,看看它们是如何协同完成工做的post

  1. 遇到 var a, 编译器会询问做用域是否已经有一个该名称的变量存在于同一个做用域的集合中。 若是是, 编译器会忽略该声明, 继续进行编译; 不然它会要求做用域在当前做用域的集合中声明一个新的变量, 并命名为a。
  2. 接下来编译器会为引擎生成运行时所需的代码, 这些代码被用来处理 a = 2 这个赋值操做。 引擎运行时会首先询问做用域, 在当前的做用域集合中是否存在一个叫做 a 的变量。 若是是, 引擎就会使用这个变量; 若是否, 引擎会继续查找该变量。若是引擎最终找到了 a 变量, 就会将 2 赋值给它。 不然引擎就会举手示意并抛出一个异常!

简单来讲,变量的赋值操做会执行两个动做, 首先编译器会在当前做用域中声明一个变量(若是以前没有声明过), 而后在运行时引擎会在做用域中查找该变量, 若是可以找到就会对它赋值,不然就会并抛出一个异常。学习

做用域嵌套

咱们知道引擎查找变量的过程在做用域中进行的,而这个过程一般会涉及多个做用域。spa

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

为了便于理解,能够将做用域嵌套比喻成一栋高楼,咱们从一楼(当前做用域)开始查找,若是没有找到,就会前往上一个楼层继续查找,以此类推。一旦到达顶层(全局做用域),可能找到,也可能没有找到,查找过程都必须中止。

LHS查询和RHS查询

继续上文的示例,引擎在执行编译器生成的代码时,会经过查找变量 a 来判断它是否已经声明过。查找的过程由做用域进行协助, 可是引擎执行怎样的查找, 会影响最终的查找结果。查找过程分为两类:LHS查询和RHS查询。其实很简单,当变量出如今赋值操做的左侧时进行 LHS 查询, 出如今右侧时进行 RHS 查询
更准确一点的讲, RHS 查询是查找某个变量的值,而 LHS 查询是查找变量的容器自己,从而能够对其赋值。以下面的示例:

var a = 2; // a: LHS查询
var b = 3; // b: LHS查询
a = b; //a: LHS查询 b:RHS查询

为何区分 LHS 和 RHS 是一件重要的事情?

由于在变量尚未声明(在任何做用域中都没法找到该变量) 的状况下,  LHS 和 RHS两种查询的行为是不同的

1.当引擎执行 RHS 查询时,若是 RHS 查询在全部嵌套的做用域中遍寻不到所需的变量, 引擎就会抛出 ReferenceError异常。 

console.log(a); //ReferenceError: a is not defined

2.当引擎执行 LHS 查询时, 若是在顶层(全局做用域) 中也没法找到目标变量,全局做用域中就会建立一个具备该名称的变量, 并将其返还给引擎, 前提是程序运行在非“严格模式” 下。若是在“严格模式”下,引擎也会抛出 ReferenceError异常。

//非严格模式
var a =2;
b = a;
console.log(b); //2
//严格模式
'use strict';
var a =2;
b = a;
console.log(b); //ReferenceError: b is not defined

另外,若是 RHS 查询找到了一个变量, 可是你尝试对这个变量的值进行不合理的操做,好比试图对一个非函数类型的值进行函数调用, 或着引用 null 或 undefined 类型的值中的属性, 那么引擎会抛出另一种类型的异常, 叫做TypeError。

ReferenceError 表明做用域判别失败相关, 而 TypeError 则表明做用域判别成功了, 可是对结果的操做是非法或不合理的。

参考

《你不知道的JavaScript》

做者:CoderFocus

微信公众号:

声明:本文为博主学习感悟总结,水平有限,若是不当,欢迎指正。若是您认为还不错,不妨点击一下下方的推荐按钮,谢谢支持。转载与引用请注明做者及出处。

相关文章
相关标签/搜索