LHS和RHS查询
当变量出现在赋值操作的左侧时进行 LHS 查询,出现在右侧时进行 RHS 查询。RHS 查询与简单地查找某个变量的值别无二致,而 LHS 查询则是试图找到变量的容器本身,从而可以对其赋值。如果查找的目的是对变量进行赋值,那么就会使用 LHS 查询;如果目的是获取变量的值,就会使用 RHS 查询。
如果 RHS 查询在所有嵌套的作用域中遍寻不到所需的变量,引擎就会抛出 ReferenceError 异常。值得注意的是,ReferenceError 是非常重要的异常类型。
相较之下,当引擎执行 LHS 查询时,如果在顶层(全局作用域)中也无法找到目标变量,
全局作用域中就会创建一个具有该名称的变量,并将其返还给引擎,前提是程序运行在非 “严格模式”下。
ReferenceError 同作用域判别失败相关,而 TypeError 则代表作用域判别成功了,但是对 结果的操作是非法或不合理的
词法作用域
JavaScript 中有两个机制可以“欺骗”词法作用域:eval(..) 和 with。前者可以对一段包 含一个或多个声明的“代码”字符串进行演算,并借此来修改已经存在的词法作用域(在 运行时)。后者本质上是通过将一个对象的引用当作作用域来处理,将对象的属性当作作 用域中的标识符来处理,从而创建了一个新的词法作用域(同样是在运行时)。
这两个机制的副作用是引擎无法在编译时对作用域查找进行优化,因为引擎只能谨慎地认 为这样的优化是无效的。使用这其中任何一个机制都将导致代码运行变慢。不要使用它们。
函数作用域和块级作用域
区分函数声明和表达式最简单的方法是看 function 关键字出现在声明中的位 置(不仅仅是一行代码,而是整个声明中的位置)。如果 function 是声明中 的第一个词,那么就是一个函数声明,否则就是一个函数表达式。
UMD模式
1 | (function IIFE( def ) { |
1 | (function (root, factory) { |
提升
函数声明和变量声明都会被提升。但是一个值得注意的细节(这个细节可以出现在有多个
“重复”声明的代码中)是函数会首先被提升,然后才是变量。
异步
程序中将来执行的部分并不一定在现在 运行的部分执行完之后就立即执行。换句话说,现在无法完成的任务将会异步完成。
在某些条件下,某些浏览器的 console.log(..) 并不会把传入的内容立 即输出。出现这种情况的主要原因是,在许多程序(不只是 JavaScript)中,I/O 是非常低 速的阻塞部分。所以,(从页面 /UI 的角度来说)浏览器在后台异步处理控制台 I/O 能够提 高性能,这时用户甚至可能根本意识不到其发生。
“事件”(JavaScript 代码执行)调度总是由包含它的环境进行。
协作
1 | var res = []; |
任务
我认为对于任务队列最好的理解方式就是,它是挂在事件循环队列的每个 tick 之后 的一个队列。在事件循环的每个 tick 中,可能出现的异步动作不会导致一个完整的新事件 添加到事件循环队列中,而会在当前 tick 的任务队列末尾添加一个项目(一个任务)。
省点回调
还有一种常见的回调模式叫作“error-first 风格”(有时候也称为“Node 风格”,因为几乎 所有 Node.js API 都采用这种风格),其中回调的第一个参数保留用作错误对象(如果有的 话)。如果成功的话,这个参数就会被清空 / 置假(后续的参数就是成功数据)。不过,如果产生了错误结果,那么第一个参数就会被置起 / 置真(通常就不会再传递其他结果):