[TOC]
## 1.1 作用域
几乎所有编程语言最基本的功能之一,就是能储存变量当中的值,并且能在之后对这个值进行访问或修改。事实上,这是这种储存和访问变量的值的能力将**状态**带给了程序。
变量储存在哪?程序需要时如何找到变量?这些问题说明需要一套设计良好的规则来存储变量,并且之后可以方便地找到这些变量。这套规则被成为**作用域**。
### 1.1.1 编译原理
尽管通常将JavaScript 归类为“动态”或“解释执行”语言,但事实上它是一门**编译语言**。
与传统编译语言流程(词法分析-语法分析-代码生成)不同:
* JavaScript引擎复杂得多,如,在语法分析和代码生成阶段有特定的步骤来对运行性能进行优化,包括对冗余元素进行优化等。
* JavaScript 引擎不会有大量的(像其他语言编译器那么多的)时间用来进行优化,因为与其他语言不同,**JavaScript 的编译过程不是发生在构建之前的**。
* 对于JavaScript 来说,大部分情况下编译发生在代码执行前的几微秒(甚至更短)的时间内。
### 1.1.2 理解作用域
* 引擎:从头到尾负责整个JavaScript 程序的编译及执行过程。
* 编译器:负责语法分析及代码生成等。
* 作用域:负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。
**1. 一个赋值操作的过程**
变量的赋值操作会执行两个动作,首先编译器会在当前作用域中声明一个变量(如果之前没有声明过),然后在运行时引擎会在作用域中查找该变量,如果能够找到就会对它赋值。
**2. LHS与RHS查询**
* RHS 查询与简单地查找某个变量的值别无二致(目的是获取变量的值);
* LHS 查询则是试图找到变量的容器本身,从而可以对其赋值。(目的是对变量赋值)
~~~
function foo(a) {
// 这里隐式包含了 a = 2 这个赋值,所以对 a 进行了 LHS 查询
var b = a;
// 这里对 a 进行了 RHS 查询,找到 a 的值,然后对 b 进行 LHS 查询,把 2 赋值给 b
return a + b;
// 这里包含了对 a 和 b 进行的 RHS 查询
}
var c = foo(2);
// 这里首先对 foo 进行 RHS 查询,找到它是一个函数,然后对 c 进行 LHS 查询把 foo 赋值给 c
以上共3LHS,4RHS
~~~
**3. 作用域嵌套**
当一个块或函数嵌套在另一个块或函数中时,就发生了作用域的嵌套。因此,在当前作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量,或抵达最外层的作用域(也就是全局作用域)为止。
~~~
function foo(a) {
console.log( a + b );
}
var b = 2;
foo( 2 ); // 4
~~~
对b 进行的RHS 引用无法在函数foo 内部完成,但可以在上一级作用域(在这个例子中就是全局作用域)中完成。
引擎从当前的执行作用域开始查找变量,如果找不到,就向上一级继续查找。当抵达最外层的全局作用域时,无论找到还是没找到,查找过程都会停止。
**4. 异常**
在变量还没有声明(在任何作用域中都无法找到该变量)的情况下,LHS查询与RHS查询的行为是不一样的。
~~~
function foo(a) {
console.log( a + b );
b = a;
}
foo( 2 );
~~~
第一次对b 进行RHS 查询时是无法找到该变量的。也就是说,这是一个“未声明”的变量,因为在任何相关的作用域中都无法找到它。
**不成功的RHS 引用会导致抛出ReferenceError 异常。不成功的LHS 引用会导致自动隐式地创建一个全局变量(非严格模式下),该变量使用LHS 引用的目标作为标识符,或者抛出ReferenceError 异常(严格模式下)。**
如果RHS 查询找到了一个变量,但是你尝试对这个变量的值进行不合理的操作,比如试图对一个非函数类型的值进行函数调用,或着引用null 或undefined 类型的值中的属性,那么引擎会抛出另外一种类型的异常,叫作`TypeError`。
ReferenceError 同作用域判别失败相关,而TypeError 则代表作用域判别成功了,但是对结果的操作是非法或不合理的。
- 前言
- 第一章 JavaScript简介
- 第三章 基本概念
- 3.1-3.3 语法、关键字和变量
- 3.4 数据类型
- 3.5-3.6 操作符、流控制语句(暂略)
- 3.7函数
- 第四章 变量的值、作用域与内存问题
- 第五章 引用类型
- 5.1 Object类型
- 5.2 Array类型
- 5.3 Date类型
- 5.4 基本包装类型
- 5.5 单体内置对象
- 第六章 面向对象的程序设计
- 6.1 理解对象
- 6.2 创建对象
- 6.3 继承
- 第七章 函数
- 7.1 函数概述
- 7.2 闭包
- 7.3 私有变量
- 第八章 BOM
- 8.1 window对象
- 8.2 location对象
- 8.3 navigator、screen与history对象
- 第九章 DOM
- 9.1 节点层次
- 9.2 DOM操作技术
- 9.3 DOM扩展
- 9.4 DOM2和DOM3
- 第十章 事件
- 10.1 事件流
- 10.2 事件处理程序
- 10.3 事件对象
- 10.4 事件类型
- 第十一章 JSON
- 11.1-11.2 语法与序列化选项
- 第十二章 正则表达式
- 12.1 创建正则表达式
- 12.2-12.3 模式匹配与RegExp对象
- 第十三章 Ajax
- 13.1 XMLHttpRequest对象
- 你不知道的JavaScript
- 一、作用域与闭包
- 1.1 作用域
- 1.2 词法作用域
- 1.3 函数作用域与块作用域
- 1.4 提升
- 1.5 作用域闭包
- 二、this与对象原型
- 2.1 关于this
- 2.2 全面解析this
- 2.3 对象
- 2.4 混合对象“类”
- 2.5 原型
- 2.6 行为委托
- 三、类型与语法
- 3.1 类型
- 3.2 值
- 3.3 原生函数