[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);
```
![](https://box.kancloud.cn/5d8a250bf6ecd2c9c316763ecc9fe17a_1247x536.png)
**作用域链的作用**
>[info]通过作用域链结构,可以分析出代码中变量的取值情况。
变量的取值规则是:首先当前作用域中寻找值,找到了则取当前作用域的值,找不到则向上一级作用寻找值,找到了则取值,找不到则继续向上一级作用寻找值,依次类推,直到寻找到最顶层作用域,即全局作用域,如果还是找不到值,则报错(... is not defined)。在作用域链中访问变量的过程,如下图
![](https://box.kancloud.cn/6da1e9cdc0508cb912cd72bbfee53fdc_921x202.png)
```
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. 调用
> 定义好的函数可以直接调用,也可以当做另一个函数的实参、返回值进行传递,传递过程中可以调用。