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

第一个问题: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. 【SpringBoot】Re 02 Import与自定义装配实现

    Import的注册形式: 1.使用@Import导入一个或者多个类字节对象 @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME ...

  2. jdk命令行工具系列——检视阅读

    jdk命令行工具系列--检视阅读 参考 java虚拟机系列 RednaxelaFX知乎问答 RednaxelaFX博客 jps--虚拟机进程状态工具 jps :(JVM Process Status ...

  3. tmux开启鼠标模式

    在tmux的配置文件中进行配置: vim ~/.tmux.conf set -g mouse on

  4. LeetCode279:完全平方数——动态规划算法——python语言

    无意间看到了这么一个题: LeetCode279:完全平方数,动态规划解法超过46%,作弊解法却超过97% ============================================= ...

  5. AQS源码深度解析之cancelAcquire方法解读

    1.背景 2.源码解读 调用该方法的地方 方法源码解读 /** * 取消获取资源(异常处理时都需要用到) * 方法主要功能: * 1.处理当前取消节点的状态: * 2.将当前取消节点的前置非取消节点和 ...

  6. 图扑 HT for Web 轻松构建组态拓扑结构

      在现代的数据可视化和网络管理中,拓扑图是一种非常重要的工具.它可以直观地展示节点(Node)和节点之间的关系(Edge).无论是在 2D 还是 3D 环境中,拓扑图都可以帮助我们更好地理解和管理复 ...

  7. List按需转换Map

    需求 : Mybatis的<select>返回一个List,想按照实体类其中的属性转换成Map<String, String>实现过程: 其实有很多方式,可以使用普通for循环 ...

  8. 下一代浏览器和移动自动化测试框架:WebdriverIO

    1.介绍 今天给大家推荐一款基于Node.js编写且号称下一代浏览器和移动自动化测试框架:WebdriverIO 简单来讲:WebdriverIO 是一个开源的自动化测试框架,它允许测试人员使用 No ...

  9. 微服务开发手册之GRPC 荐

    GRPC是一个高性能.通用的开源RPC框架,基于HTTP/2协议标准和Protobuf序列化协议开发,支持众多的开发语言. @[TOC] 1 简介 在GRPC框架中,客户端可以像调用本地对象一样直接调 ...

  10. css flex属性

    css学的不咋熟,搞一个复杂一点的水平居中,用display 属性 + position属性 + float属性,搞了好久居然没搞出来,然后我去翻资料,发现我最不常用的flex能解决这个问题,于是我就 ...