[TOC]
## 作用域
定義:變量或函數(shù)可以起作用的范圍。
### 全局作用域
  script標(biāo)簽或js文件中所包含的范圍(區(qū)域)叫全局作用域。在全局作用域中定義的變量稱(chēng)為**全局變量**,全局變量在任何位置都可以訪(fǎng)問(wèn)。
### 局部作用域
  一個(gè)函數(shù)的內(nèi)部所包含的范圍(區(qū)域)叫局部作用域。在局部作用域中定義的變量稱(chēng)為**局部變量**,局部變量只能在當(dāng)前的局部作用域中訪(fǎng)問(wèn),函數(shù)參數(shù)也是局部變量。
>   注意:不使用var聲明的變量是全局變量,不推薦使用。
### 塊級(jí)作用域
任何一對(duì)花括號(hào)({代碼})中的語(yǔ)句集都屬于一個(gè)塊,在這之中定義的所有變量在代碼塊外都是不可見(jiàn)的,我們稱(chēng)之為塊級(jí)作用域。
**在es5之前沒(méi)有塊級(jí)作用域的的概念,只有函數(shù)作用域**,現(xiàn)階段可以認(rèn)為JavaScript沒(méi)有塊級(jí)作用域
<br>
## 作用域鏈
### 概念:
  只有函數(shù)可以制造作用域結(jié)構(gòu)。在JavaScript中只要是代碼,就至少有一個(gè)作用域, 即全局作用域(script標(biāo)簽或外部js文件中)。凡是代碼中有函數(shù),那么這個(gè)函數(shù)就構(gòu)成另一個(gè)作用域。如果函數(shù)中還有函數(shù),那么在這個(gè)作用域中就又可以誕生一個(gè)作用域。將這樣的所有的作用域列出來(lái),可以有一個(gè)結(jié)構(gòu): 函數(shù)內(nèi)指向函數(shù)外的鏈?zhǔn)浇Y(jié)構(gòu)。就稱(chēng)作作用域鏈。
```
// 案例
var num = 55; //全局作用域 --0級(jí)鏈
function fn1() { //局部作用域 --1級(jí)鏈
var num = 66;
console.log(num);
function fn2() { //局部作用域 --2級(jí)鏈
console.log(num);
}
fn2();
}
fn1();
console.log(num);
```

**作用域鏈的作用**
>[info]通過(guò)作用域鏈結(jié)構(gòu),可以分析出代碼中變量的取值情況。
變量的取值規(guī)則是:首先當(dāng)前作用域中尋找值,找到了則取當(dāng)前作用域的值,找不到則向上一級(jí)作用尋找值,找到了則取值,找不到則繼續(xù)向上一級(jí)作用尋找值,依次類(lèi)推,直到尋找到最頂層作用域,即全局作用域,如果還是找不到值,則報(bào)錯(cuò)(... is not defined)。在作用域鏈中訪(fǎng)問(wèn)變量的過(guò)程,如下圖

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