[TOC] ## 作用域 定义:变量或函数可以起作用的范围。 ### 全局作用域 &ensp;&emsp;script标签或js文件中所包含的范围(区域)叫全局作用域。在全局作用域中定义的变量称为**全局变量**,全局变量在任何位置都可以访问。 ### 局部作用域 &ensp;&emsp;一个函数的内部所包含的范围(区域)叫局部作用域。在局部作用域中定义的变量称为**局部变量**,局部变量只能在当前的局部作用域中访问,函数参数也是局部变量。 > &ensp;&emsp;注意:不使用var声明的变量是全局变量,不推荐使用。 ### 块级作用域 任何一对花括号({代码})中的语句集都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域。 **在es5之前没有块级作用域的的概念,只有函数作用域**,现阶段可以认为JavaScript没有块级作用域 <br> ## 作用域链 ### 概念: &ensp;&emsp;只有函数可以制造作用域结构。在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> ## 预解析 &ensp;&emsp;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. 调用 > 定义好的函数可以直接调用,也可以当做另一个函数的实参、返回值进行传递,传递过程中可以调用。