JavaScript 作用域不完全指北
什么是作用域
对于几乎所有编程语言,最基本的功能之一就是能够存储变量的值,并且能在之后对这个值进行访问和修改。这样就会带来几个问题,这些变量存储在哪里?程序在需要的时候又是如何找到它们的?要解决这些问题,就需要引入一套规则来存储变量和访问变量,这套规则就是作用域。
理解作用域
在刚开始接触 JavaScript 这门语言时,肯定会经常接触到 JavaScript 是动态语言, 是解释执行的,但事实上 JavaScript 是一门编译语言。只不过和 Java、C# 这些传统意义上的编译语言不同,JavaScript 的编译过程不是发生在构建之前的。大部分情况下,JavaScript 的编译过程发生在代码执行前的很短时间内。也就是说 JavaScript 代码在执行前都要进行编译。
为了更好地理解作用域,我们需要明确下面几个概念
- 引擎
从头到尾负责整个 JavaScript 程序的编译及执行过程
- 编译器
负责语法分析及代码生成等脏活累活
- 作用域
负责收集并维护由所有声明的标识符(变量) 组成的一系列查询, 并实施一套非常严格的规则, 确定当前执行的代码对这些标识符的访问权限。
下面我们从引擎、编译器和作用域的角度,分析 var a = 2 这条声明语句,看看它们是如何协同完成工作的
- 遇到 var a, 编译器会询问作用域是否已经有一个该名称的变量存在于同一个作用域的集合中。 如果是, 编译器会忽略该声明, 继续进行编译; 否则它会要求作用域在当前作用域的集合中声明一个新的变量, 并命名为a。
- 接下来编译器会为引擎生成运行时所需的代码, 这些代码被用来处理 a = 2 这个赋值操作。 引擎运行时会首先询问作用域, 在当前的作用域集合中是否存在一个叫作 a 的变量。 如果是, 引擎就会使用这个变量; 如果否, 引擎会继续查找该变量。如果引擎最终找到了 a 变量, 就会将 2 赋值给它。 否则引擎就会举手示意并抛出一个异常!
简单来说,变量的赋值操作会执行两个动作, 首先编译器会在当前作用域中声明一个变量(如果之前没有声明过), 然后在运行时引擎会在作用域中查找该变量, 如果能够找到就会对它赋值,否则就会并抛出一个异常。
作用域嵌套
我们知道引擎查找变量的过程在作用域中进行的,而这个过程通常会涉及多个作用域。
当一个块或函数嵌套在另一个块或函数中时, 就发生了作用域的嵌套。 因此, 在当前作用域中无法找到某个变量时, 引擎就会在外层嵌套的作用域中继续查找, 直到找到该变量,或抵达最外层的作用域(也就是全局作用域) 为止。
为了便于理解,可以将作用域嵌套比喻成一栋高楼,我们从一楼(当前作用域)开始查找,如果没有找到,就会前往上一个楼层继续查找,以此类推。一旦到达顶层(全局作用域),可能找到,也可能没有找到,查找过程都必须停止。
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》
JavaScript 作用域不完全指北的更多相关文章
- JavaScript 词法作用域不完全指北
在 JavaScript 作用域不完全指北 中,我们介绍了作用域的概念以及 JavaScript 引擎.编译器和作用域的关系.作用域有两种主要的工作模型:词法作用域和动态作用域.其中最为普遍的也是大多 ...
- c#封装DBHelper类 c# 图片加水印 (摘)C#生成随机数的三种方法 使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象 c# 制作正方形图片 JavaScript 事件循环及异步原理(完全指北)
c#封装DBHelper类 public enum EffentNextType { /// <summary> /// 对其他语句无任何影响 /// </summary> ...
- JavaScript作用域
JavaScript作用域 JavaScript作用域一直是前端开发的难题,现在只要用五句话就可解决. 一.“JavaScript中无块级作用域” 在Java或C#中存在块级作用域,即:大括号也是一个 ...
- 关于Javascript作用域及作用域链的总结
本文是根据以下文章以及<Javascript高级程序设计(第三版)>第四章相关内容总结的. 1.Javascript作用域原理,地址:http://www.laruence.com/200 ...
- [译] 你该知道的javascript作用域 (javascript scope)(转)
javascript有一些对于初学者甚至是有经验的开发者都难以理解的概念. 这个部分是针对那些听到 : 作用域, 闭包, this, 命名空间, 函数作用域, 函数作用域, 全局作用域, 变量作用域( ...
- Python自动化 【第十六篇】:JavaScript作用域和Dom收尾
本节内容: javascript作用域 DOM收尾 JavaScript作用域 JavaScript的作用域一直以来是前端开发中比较难以理解的知识点,对于JavaScript的作用域主要记住几句话,走 ...
- JavaScript作用域闭包简述
JavaScript作用域闭包简述 作用域 技术一般水平有限,有什么错的地方,望大家指正. 作用域就是变量起作用的范围.作用域包括全局作用域,函数作用域以块级作用域,ES6中的let和const可以形 ...
- JavaScript 作用域和变量提升
本文是这篇文章的简单翻译. 如果按照下面的代码按照JavaScript程序的执行方式执行,alert函数会显示什么? var foo = 1; function bar() { if (!foo) { ...
- javascript作用域和闭包之我见
javascript作用域和闭包之我见 看了<你不知道的JavaScript(上卷)>的第一部分--作用域和闭包,感受颇深,遂写一篇读书笔记加深印象.路过的大牛欢迎指点,对这方面不懂的同学 ...
随机推荐
- 0629 Flink Meetup 北京站 PPT下载
工程实用问题解决方案介绍,实用
- 【未完成】【Mybatis】字段由字符+数字变量组成,但要对变量进行计算
??????????????????????
- Laravel 推荐-基础入门+实战+拓展视频教程
1.laravel基础 1.Composer:类库管理神器 - Laravel的安装和配置 2.路由:地址和方法的小媒人 - 基础路由 3.控制器:管家婆 - 基础用法 4.视图:最终结果输出 5.b ...
- sed和awk练习及知识点
一.针对/etc/passwd操作 1.sed操作,将文件中的第九行至第十五行复制到第16行下. [root@ns1 lianxi]# sed '9,15H;16G' /etc/passwd 2.用a ...
- cookie清除及其他操作
JavaScript是运行在客户端的脚本,因此一般是不能够设置Session的,因为Session是运行在服务器端的. 而cookie是运行在客户端的,所以可以用JS来设置cookie. 一:设置co ...
- 开源推荐 - CoDo开源一站式DevOps平台
一群有梦想的年轻人开源了一个云管理平台,他们的口号是:让天下没有996的运维 有幸参与到CoDo项目的开发,这是一个非常棒的一站式开源运维平台,分享给大家 平台介绍 CODO是一款为用户提供企业多混合 ...
- 认识beanstalkd
认识beanstalkd 背景 公司业务做某个需求是将数据写入到消息队列中,然后另外一个服务来消费数据,这里的消息队列使用的是beastalkd,之前接触到的消息队列为kafka,因此简单学习记录一 ...
- idea maven项目打包并部署到tomcat
打包 打开Maven管理器,邮寄package,执行Run Maven Build,执行成功后将war包生成到target目录下. 部署 1.将war包复制到tomcat安装目录下的webapps目录 ...
- java web开发入门十(idea创建maven SSM项目)基于intellig idea
一.搭建项目骨架 二.配置pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xm ...
- ConcurrentHashMap竟然也有死循环问题?
前几天和朋友闲聊,说遇到了一个ConcurrentHashMap死循环问题,当时心里想这不科学呀?ConcurrentHashMap怎么还有死循环呢,毕竟它已经解决HashMap中rehash中死循环 ...
