[TOC]
## 作用域
定義:變量或函數可以起作用的范圍。
### 全局作用域
  script標簽或js文件中所包含的范圍(區域)叫全局作用域。在全局作用域中定義的變量稱為**全局變量**,全局變量在任何位置都可以訪問。
### 局部作用域
  一個函數的內部所包含的范圍(區域)叫局部作用域。在局部作用域中定義的變量稱為**局部變量**,局部變量只能在當前的局部作用域中訪問,函數參數也是局部變量。
>   注意:不使用var聲明的變量是全局變量,不推薦使用。
### 塊級作用域
任何一對花括號({代碼})中的語句集都屬于一個塊,在這之中定義的所有變量在代碼塊外都是不可見的,我們稱之為塊級作用域。
**在es5之前沒有塊級作用域的的概念,只有函數作用域**,現階段可以認為JavaScript沒有塊級作用域
<br>
## 作用域鏈
### 概念:
  只有函數可以制造作用域結構。在JavaScript中只要是代碼,就至少有一個作用域, 即全局作用域(script標簽或外部js文件中)。凡是代碼中有函數,那么這個函數就構成另一個作用域。如果函數中還有函數,那么在這個作用域中就又可以誕生一個作用域。將這樣的所有的作用域列出來,可以有一個結構: 函數內指向函數外的鏈式結構。就稱作作用域鏈。
```
// 案例
var num = 55; //全局作用域 --0級鏈
function fn1() { //局部作用域 --1級鏈
var num = 66;
console.log(num);
function fn2() { //局部作用域 --2級鏈
console.log(num);
}
fn2();
}
fn1();
console.log(num);
```

**作用域鏈的作用**
>[info]通過作用域鏈結構,可以分析出代碼中變量的取值情況。
變量的取值規則是:首先當前作用域中尋找值,找到了則取當前作用域的值,找不到則向上一級作用尋找值,找到了則取值,找不到則繼續向上一級作用尋找值,依次類推,直到尋找到最頂層作用域,即全局作用域,如果還是找不到值,則報錯(... is not defined)。在作用域鏈中訪問變量的過程,如下圖

```
var num = 555;
function fn1() {
var num = 666;
console.log(num); // 輸出結果:666
function fn2() {
var num = 777;
console.log(num); // 輸出結果: 777
}
fn2()
}
fn1();
function fn3() {
console.log(num); // 輸出結果:555;局部變量在退出之后會銷毀,故 num 的值仍為 555,
function fn4() {
console.log(num); // 輸出結果:555
}
fn4()
}
fn3();
```
<br>
<br>
## 預解析
  JavaScript代碼的執行是由瀏覽器中的JavaScript解析器來執行的。JavaScript解析器執行JavaScript代碼的時候,分為兩個階段,預解析階段(預處理、預編譯)與執行階段。
### 預解析的作用
1. 提升變量聲明:把變量的聲明提升到當前作用域的最前面,只會提升聲明,不會提升賦值。
```
//預解析之前的代碼
console.log(a,b,'========開始========');
var a = 1;
var b = 2;
console.log(a,b,'========結束========');
//預解析之后的代碼
var a ;
var b ;
console.log(a,b,'========開始========');
a = 1;
b = 2;
console.log(a,b,'========結束========');
```
2. 提升函數聲明:把函數的聲明提升到當前作用域的最前面,只會提升聲明,不會提升調用。
**當函數有實參時,因為函數不調用不執行,故預解析時要先傳入參數在繼續解析。**
```
//預解析之前的代碼
fn1();
console.log('========結束========');
function fn1() {
console.log(1);
}
function fn2() {
console.log(2);
}
console.log('========結束========');
//預解析之后的代碼
function fn1() {
console.log(1);
}
function fn2() {
console.log(2);
}
fn1();
console.log('========開始========');
console.log('========結束========');
```
#### 分析代碼
**例1、**
在預解析之前的代碼
```
var a = 25;
function abc() {
alert(a);
var a = 10;
}
abc();
```
與解析后的代碼
```
var a;
function abc() {
var a;
alert(a);
a = 10;
}
a =25;
abc();
```
**例2、**
在預解析之前的代碼
```
console.log(a);
function a() {
console.log('aaaaa');
}
var a = 1;
console.log(a);
```
預解析后的代碼
```
var a;
function a() {
console.log('aaaaa');
}
console.log(a);
a = 1;//源代碼中,有變量a和函數a,此時a優先作為變量名,同時預解析先提升變量的聲明。
console.log(a);
//1、先提升var,再提升function;
//2、如果變量名和函數名相同,則該名稱優先作為函數名使用;
```
**練習1**
```
var num = 10;
fun();
function fun() {
console.log(num);
var num = 20;
}
//預解析
var num;
function fun() {
var num;
console.log(num);
num = 20;
}
num = 10;
fun();
```
**練習2**
```
//預解析之前的代碼
f1();
console.log(c);
console.log(b);
console.log(a);
function f1() {
// 同時給多個變量賦值,var只修飾最近的一個變量名
var a = b = c = 9;
console.log(a);
console.log(b);
console.log(c);
}
//預解析
function f1() {
var a;
// 同時給多個變量賦值,var只修飾最近的一個變量名
a = 9;
b = 9;
c = 9;
console.log(a); //輸出結果:9
console.log(b); //輸出結果:9
console.log(c); //輸出結果:
9
}
f1();
console.log(c); //輸出結果:9
console.log(b); //輸出結果:9
console.log(a); //輸出結果:undefined,在函數f1中,var只修飾最近的一個變量名
,即a,所以b和c都是全局變量,a是局部變量,當退出函數f1時,a被銷毀,所以輸出結果是undefined
```
### **小結**
>[info] **一、 變量**
> 1. 定義,書寫位置
> a、寫在js文件中、`<script>`標簽中,叫做全局變量,在任何位置都能使用。作用范圍叫做全局作用域。
> b、寫在函數形參、函數體中,叫做局部變量,只能在函數中使用。作用范圍叫做局部作用域。
> 2. 特點
> 不管是全局變量還是局部變量,都有對應的作用域,它們的作用范圍受限于對應的作用域。
> 3. 使用
> 定義好的變量可以賦值給其他變量,也可以當做函數的參數來使用。
>
> **二、 函數**
> 1. 定義,書寫位置
> a、函數的定義方式可以是命名函數、匿名函數;
> b、兩種定義方式都能寫在js文件中,`<script>`標簽中,函數中;
> 2. 特點
> 類似于變量的作用域,函數也有作用范圍,函數只能在當前作用域中調用;
> 3. 調用
> 定義好的函數可以直接調用,也可以當做另一個函數的實參、返回值進行傳遞,傳遞過程中可以調用。