这本书之前囫囵地看了一遍,确实点明了很多以前不清不楚的点,但是仅仅看一遍是没什么用的,最近面试遇到不少原理相关的题感觉答得不理想,回头看下其实以前都理解过,但是没有记下来,正好结合实际的问题来再学习一下书上的内容。

第一个问题:JavaScript是如何查找变量的?

第1部分 作用域和闭包

第1章 作用域是什么?

编译原理

这本讲解JavaScript的书首先讲的却是编译原理,一开始看起来让人费解,但实际上从后面内容我们可以发现,JavaScript的很多特性都与编译原理有着极大的关系。我们通常称JavaScript是动态解释执行语言,因为它不是提前编译的,而是根据执行时的情况来对代码进行处理。

在传统编译语言的流程中,代码的执行通常分成三个步骤:

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

将语句分解成词法单元(Token),例如 var a = 2; 会被分解成: var、a、=、2、;。需要注意的是分词和词法分析有少许的区别,

如果词法单元生成器在判断a是一个独立的词法单元还是其他词法单元的一部分时,调用的是有状态的解析规则,那么这个过程就被称为词法分析。

理解不了这一句中的 有状态的解析规则

  1. 解析、语法分析(Parsing)

将词法单元流转换成树形结构的的过程。这个树被称为“抽象语法树”(Abstract Syntax Tree,AST).

  1. 代码生成

将AST转换成可执行代码的过程。简单来说就是把 var a = 2; 的AST转化为一组机器指令,用来创建一个叫做a的变量(包括分配内存),并将一个值储存在a中。

与传统编译语言的编译器相比,JavaScript引擎要更复杂,最明显的区别就体现在编译时间上,JavaScript引擎没有时间进行优化,编译过程不是在构建之前,通常发生在代码执行前的几微秒内。在作用域的背后,JavaScript引擎使用了各种方法(比如JIT延迟编译甚至实施重编译)来保证性能。

理解作用域

在理解作用域之前还有一些前置的概念需要理解,在JavaScript执行的时候有三个重要组件:

  • 引擎:负责整个JavaScript程序的编译及执行过程
  • 编译器:负责语法分析和代码生成
  • 作用域:负责收集维护由所有声明的标识符(也就是变量)组成的查询,并根据严格的规则确定执行的代码对变量的访问权限

还是上面的var a = 2;这个例子,编译器在处理时会分成两步:

  1. var a,编译器先询问作用域同一个作用域中是否已经有这样一个名称的变量。如果是,编译器则忽略这个声明,继续编译;如果否,编译器则要求作用域在当前作用域的集合中声明一个新的变量,命名为a。
  2. a = 2,编译器会为引擎生成运行所需要的的代码来执行这个赋值操作。引擎运行时同样先询问作用域,当前作用域集合中是否有一个叫做a的变量。如果是,引擎就会使用这个变量;如果否,引擎会继续查找该变量了(如何查我们在下一节说明,因为查找的是其他作用域的)。

如果再细致地说明引擎的查找过程可以把它分成LHS查询和RHS查询,“L”和“R”很好理解,就是左侧和右侧,具体来说就是一个赋值操作的左侧和右侧

变量在赋值操作左侧时就进行LHS查询,出现在右侧时就进行RHS查询,但实际上会这么简单吗?肯定不是,LHS其实是找到变量的容器本身并对其赋值,RHS则相反,意思比较接近于“取到它的原值”或“得到某某的值”。看下面这个例子:

console.log(a);

这里对a的引用就是一个RHS引用,因为没有为a赋任何值,而是获取了a的值并传递给了console.log()。而相比a = 2,很明显就是把“= 2”交给了“a”。

也就是说LHS和RHS并不是简单的左侧和右侧,而是“赋值操作的目标是谁”和“谁是操作的源头”。

理解了之后再看这个例子,除了一个RHS操作是否还能找出一个LHS操作呢?

function foo(a){
console.log(a); // 2
} foo(2);

这是一个很容易被忽略的细节,代码中存在一个隐式的a = 2操作,2倍当做参数传递给了foo()函数,这里要给参数a分配值,所以需要一次LHS查询。

作用域嵌套

这个问题说起来很简单,就是当引擎需要变量时,会先在当前作用域寻找,如果没有这个变量就到上一级作用域查找,直到最外层,也就是全局作用域,到达这里以后即使没找到也会停下来。

function foo(a){
console.log(a + b); // 2
} var b = 2; foo(2); // 4

异常

上面我们费了半天劲来理解LHS和RHS有什么意义呢?看下面这段代码:

function foo(a){
console.log(a + b);
b = a;
} foo(2);

显而易见这段代码会报异常,因为对b的RHS查询无法找到该变量,b是未声明的变量,但如果是进行LHS查询则不同,上面说了,引擎在当前作用域未能查找到对象就会向上一级,直到全局作用域还未能找到时,全局作用域就会创建一个具有该名称的变量,并将其交给引擎(前提是在非“严格模式”下)。偶尔会看到一些可以印证这一例子的不规范代码——未声明的变量被使用,如果是在ES5的“严格模式”下,会和RHS一样报ReferenceError的异常。

《你不知道的JavaScript》读书笔记(一):JS是如何查找变量的的更多相关文章

  1. 你不知道的javascript读书笔记3

    概述 这是我看<你不知道的JavaScript(中卷)>中关于类型检查的笔记,供以后开发时参考,相信对其他人也有用. typeof 我们知道js中有七种内置类型:undefined, nu ...

  2. <你不知道的JavaScript>读书笔记

    近几天看了一本不错的 JavaScript 的书,是 Kyle Simpson 写的 <You Don't know JS>.这本书是 Kyle Simpson 在 Github 上的开源 ...

  3. 你不知道的JavaScript上卷笔记

    你不知道的JavaScript上卷笔记 前言 You don't know JavaScript是github上一个系列文章   初看到这一标题的时候,感觉怎么老外也搞标题党,用这种冲突性比较强的题目 ...

  4. 【你不知道的javaScript 上卷 笔记3】javaScript中的声明提升表现

    console.log( a ); var a = 2; 执行输出undefined a = 2; var a; console.log( a ); 执行输出2 说明:javaScript 运行时在编 ...

  5. 数据结构与算法JavaScript 读书笔记

    由于自己在对数组操作这块比较薄弱,然后经高人指点,需要好好的攻读一下这本书籍,原本想这个书名就比较高深,这下不好玩了.不过看着看着突然觉得讲的东西都比较基础.不过很多东西,平时还是没有注意到,故写出读 ...

  6. 《你不知道的JavaScript》笔记(一)

    用了一个星期把<你不知道的JavaScript>看完了,但是留下了很多疑惑,于是又带着这些疑惑回头看JavaScript的内容,略有所获. 第二遍阅读这本书,希望自己能够有更为深刻的理解. ...

  7. 【你不知道的javaScript 上卷 笔记1】 javaScript 是如何工作的?

    一.什么是作用域? 作用域是用来存储变量以及方便寻找变量的一套规则. 二.javaScript 编译过程(编译发生在代码执行前的几微妙) 分词/词法分析(Tokenizing/Lexing)-> ...

  8. 【你不知道的javaScript 上卷 笔记5】javaScript中的this词法

    function foo() { console.log( a ); } function bar() { var a = 3; foo(); } var a = 2; bar(); 上面这段代码为什 ...

  9. JavaScript读书笔记(1)

    从今天开启每天看书记笔记模式,<JavaScript高级程序设计(第3版)> 1. Javascript最初是为了解决输入验证器的问题,现在已经发展成一门复杂的语言: 2.  语言标准为E ...

  10. 高性能的JavaScript -- 读书笔记

    高性能的JavaScript 一.      加载和运行 将脚本放在底部 脚本下载解析执行时,页面已经加载完成并显示在用户面前 成组脚本 减少外部脚本文件数量,整合成一个文件 延迟脚本 动态脚本元素 ...

随机推荐

  1. 【DataBase】MySQL 11 SQL函数 单行函数其五 流程控制函数 & 单行函数总结

    SQL函数其五 流程控制函数 & 单行函数总结 视频参考自:P43 - P52 https://www.bilibili.com/video/BV1xW411u7ax  单IF函数使用 -- ...

  2. 【Vue】01 基础语法

    Hello Vue的演示案例: <!DOCTYPE html> <html lang="en" xmlns:v-bind="http://www.w3. ...

  3. 【Project】JS的Map对象前后交互问题

    这是我在项目中写的一个Map对象: let map = new Map(); for (let i = 0; i < type_checked_value.length; i++) { let ...

  4. Jax框架在不同版本间的随机数生成并不一致 —— jax.random

    官方: https://jax.readthedocs.io/en/latest/api_compatibility.html#numerics-and-randomness 具体的解释就是在不同的J ...

  5. css 样式 element.style 覆盖问题

    问题: 我们在写网页定制样式的时候发现展示效果跟我们预想的不一样? 打开F12一看原来是element.style 覆盖的我定义的效果. 解决: 只要在定义的内容后面加上 !important 就行啦 ...

  6. [SDOI2010] 城市规划 题解

    前言 题目链接:洛谷. 题意简述 树套环上求至少间隔两个位置的最大独立集. (树套环,即树上每个结点都是一个结点或环) 题目分析 将题目拆解成树上 DP 和环上 DP 即可.用 tarjan 缩点就行 ...

  7. 一些八股:1.fetch 的理解。2.let、const、var

    一. 说说你对 Fetch 的理解,它有哪些优点和不足? Fetch API 是现代 JavaScript 中用于进行网络请求的接口,旨在替代传统的 XMLHttpRequest.它提供了一种更简单. ...

  8. Apache DolphinScheduler 3.1.8 保姆级教程【安装、介绍、项目运用、邮箱预警设置】轻松拿捏!

    概述 Apache DolphinScheduler 是一个分布式易扩展的可视化 DAG 工作流任务调度开源系统.适用于企业级场景,提供了一个可视化操作任务.工作流和全生命周期数据处理过程的解决方案. ...

  9. QT的基础设置(菜单栏、状态栏、任务栏。。。。)

    Qt [1] 是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架.它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器.下面介绍QT的基础配置 1 ...

  10. bat 随笔

    bat 获取文件名 %%~nxi bat 变量去除空字符 BAT批处理中的字符串处理详解(字符串截取)