[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 原生函數