《你不知道的JavaScript》读书笔记(一):JS是如何查找变量的
这本书之前囫囵地看了一遍,确实点明了很多以前不清不楚的点,但是仅仅看一遍是没什么用的,最近面试遇到不少原理相关的题感觉答得不理想,回头看下其实以前都理解过,但是没有记下来,正好结合实际的问题来再学习一下书上的内容。
第一个问题:JavaScript是如何查找变量的?
第1部分 作用域和闭包
第1章 作用域是什么?
编译原理
这本讲解JavaScript的书首先讲的却是编译原理,一开始看起来让人费解,但实际上从后面内容我们可以发现,JavaScript的很多特性都与编译原理有着极大的关系。我们通常称JavaScript是动态解释执行语言,因为它不是提前编译的,而是根据执行时的情况来对代码进行处理。
在传统编译语言的流程中,代码的执行通常分成三个步骤:
- 分词/词法分析(Tokenizing/Lexing)
将语句分解成词法单元(Token),例如 var a = 2; 会被分解成: var、a、=、2、;。需要注意的是分词和词法分析有少许的区别,
如果词法单元生成器在判断a是一个独立的词法单元还是其他词法单元的一部分时,调用的是有状态的解析规则,那么这个过程就被称为词法分析。
理解不了这一句中的 有状态的解析规则
- 解析、语法分析(Parsing)
将词法单元流转换成树形结构的的过程。这个树被称为“抽象语法树”(Abstract Syntax Tree,AST).
- 代码生成
将AST转换成可执行代码的过程。简单来说就是把 var a = 2; 的AST转化为一组机器指令,用来创建一个叫做a的变量(包括分配内存),并将一个值储存在a中。
与传统编译语言的编译器相比,JavaScript引擎要更复杂,最明显的区别就体现在编译时间上,JavaScript引擎没有时间进行优化,编译过程不是在构建之前,通常发生在代码执行前的几微秒内。在作用域的背后,JavaScript引擎使用了各种方法(比如JIT延迟编译甚至实施重编译)来保证性能。
理解作用域
在理解作用域之前还有一些前置的概念需要理解,在JavaScript执行的时候有三个重要组件:
- 引擎:负责整个JavaScript程序的编译及执行过程
- 编译器:负责语法分析和代码生成
- 作用域:负责收集维护由所有声明的标识符(也就是变量)组成的查询,并根据严格的规则确定执行的代码对变量的访问权限
还是上面的var a = 2;这个例子,编译器在处理时会分成两步:
var a,编译器先询问作用域同一个作用域中是否已经有这样一个名称的变量。如果是,编译器则忽略这个声明,继续编译;如果否,编译器则要求作用域在当前作用域的集合中声明一个新的变量,命名为a。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是如何查找变量的的更多相关文章
- 你不知道的javascript读书笔记3
概述 这是我看<你不知道的JavaScript(中卷)>中关于类型检查的笔记,供以后开发时参考,相信对其他人也有用. typeof 我们知道js中有七种内置类型:undefined, nu ...
- <你不知道的JavaScript>读书笔记
近几天看了一本不错的 JavaScript 的书,是 Kyle Simpson 写的 <You Don't know JS>.这本书是 Kyle Simpson 在 Github 上的开源 ...
- 你不知道的JavaScript上卷笔记
你不知道的JavaScript上卷笔记 前言 You don't know JavaScript是github上一个系列文章 初看到这一标题的时候,感觉怎么老外也搞标题党,用这种冲突性比较强的题目 ...
- 【你不知道的javaScript 上卷 笔记3】javaScript中的声明提升表现
console.log( a ); var a = 2; 执行输出undefined a = 2; var a; console.log( a ); 执行输出2 说明:javaScript 运行时在编 ...
- 数据结构与算法JavaScript 读书笔记
由于自己在对数组操作这块比较薄弱,然后经高人指点,需要好好的攻读一下这本书籍,原本想这个书名就比较高深,这下不好玩了.不过看着看着突然觉得讲的东西都比较基础.不过很多东西,平时还是没有注意到,故写出读 ...
- 《你不知道的JavaScript》笔记(一)
用了一个星期把<你不知道的JavaScript>看完了,但是留下了很多疑惑,于是又带着这些疑惑回头看JavaScript的内容,略有所获. 第二遍阅读这本书,希望自己能够有更为深刻的理解. ...
- 【你不知道的javaScript 上卷 笔记1】 javaScript 是如何工作的?
一.什么是作用域? 作用域是用来存储变量以及方便寻找变量的一套规则. 二.javaScript 编译过程(编译发生在代码执行前的几微妙) 分词/词法分析(Tokenizing/Lexing)-> ...
- 【你不知道的javaScript 上卷 笔记5】javaScript中的this词法
function foo() { console.log( a ); } function bar() { var a = 3; foo(); } var a = 2; bar(); 上面这段代码为什 ...
- JavaScript读书笔记(1)
从今天开启每天看书记笔记模式,<JavaScript高级程序设计(第3版)> 1. Javascript最初是为了解决输入验证器的问题,现在已经发展成一门复杂的语言: 2. 语言标准为E ...
- 高性能的JavaScript -- 读书笔记
高性能的JavaScript 一. 加载和运行 将脚本放在底部 脚本下载解析执行时,页面已经加载完成并显示在用户面前 成组脚本 减少外部脚本文件数量,整合成一个文件 延迟脚本 动态脚本元素 ...
随机推荐
- 《Python数据可视化之matplotlib实践》 源码 第一篇 入门 第二章
图 2.1 import matplotlib as mpl import matplotlib.pyplot as plt mpl.rcParams['font.sans-serif']=['Sim ...
- 连接huggingface.co报错:(MaxRetryError("SOCKSHTTPSConnectionPool(host='huggingface.co', port=443) (SSLEOFError(8, '[SSL: UNEXPECTED_EOF_WHILE_READING] EOF occurred in violation of protocol (_ssl.c:1007)
参考: https://blog.csdn.net/shizheng_Li/article/details/132942548 https://blog.csdn.net/weixin_4220944 ...
- 系统IO常用函数接口
本文整理归纳了几种常用的系统IO的函数借口,以供读者查阅使用 目录 系统IO与标准IO的区别 打开文件:open 关闭文件:close 文件读取:read 文件写入:write 位置偏移:lseek ...
- 如何在 Recovery 中启用应用
如果因为禁用了某个应用手机无法开机,那么你需要这个方法来救命 https://xdaforums.com/t/enable-or-disable-apps-directly-from-the-file ...
- CH03_布局
第3章:布局 本章目标 理解布局的原则 理解布局的过程 理解布局的容器 掌握各类布局容器的运用 理解 WPF 中的布局 WPF 布局原则 WPF 窗口只能包含单个元素.为在WPF 窗口中放置多个元 ...
- idea下spring切换jdk版本
1.首先打开项目配置设置 2. 修改project中的配置 3. 修改modules中的配置 这个方法不需要修改pom.xml文件 如果有问题请指正 及时修改 2022年9月10日16:42:16
- Windows添加软件开机自启动
两种方式 1.添加快捷方式到开始菜单 打开我的电脑找到C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup 文件夹, 如果难找的话可 ...
- Element-UI 中使用rules验证 金额 数字
data中定义验证规则:var checkCount = function (rule, val, callback) { if (!val) { return callback(new Error( ...
- P 问题和 NP 问题的简单理解
P/NP问题 | 维基百科 P 问题 P 问题的定义是:所有可以由一个确定型图灵机在多项式表达的时间内解决的问题 P 代表 Polynomial-time (adj. 多项式时间) 简单理解:答案可以 ...
- 【YashanDB知识库】汇聚库23.1环境发生coredump
[标题]汇聚库23.1环境发生coredump [问题分类]数据库错误 [关键词]YashanDB, 汇聚库, coredump [问题描述]在23.1.1.200版本数据库环境创建dblink.视图 ...