每种语言,都需要细嚼慢咽,才能发现它的美妙!在JS中,JS有两条很重要的链——原型链和作用域链
接下来一起来学习作用域和作用域链的相关知识~
1)作用域
- 什么是作用域?
- 我个人的理解就是该变量能作用的范围
- 分类:
- 全局作用域:在全局中声明的变量(全局变量)所在的环境
- 函数作用域:函数内部声明的变量(局部变量)所在的环境
这里需要注意的是,函数内的变量可以访问所在的外部变量,但是外部的变量不能访问函数的内部变量(这里要通过闭包来间接访问)
2)在介绍分析作用域中的变量前,还需要先来理解一下var声明
- var声明的特点
- 在全局中,通过var声明的变量,会直接成为window对象的属性,故可以通过window.xxx来访问该属性
- var声明的变量会被提升到全局或者函数的最顶部(var的提升机制)
var a=1;
console.log(a);//1
console.log(window.a);//1
function boost(){
console.log(a);//1
var a=1;
}
由于变量的提升机制,所以不会报错,这里实际上就进行了如下的等价操作:
function boost(){
var a=1;
console.log(a);
}
注意:对于函数内的一个变量,如果有使用var声明,则作为该函数内的局部变量,如果没有var声明,就只是一个赋值语句,并不一定是作为全局变量
下面通过一个例子来说明:
function t1(){
var d;
function t2(){
d=5;//这里在t2中没有找到,在t1中找到,故d=5,但由于此时存在函数作用域,故在全局中没有这个变量
e=6; //在t2和t1函数中都没有找到,所以这里相当于在全局中声明了window.e=6;
}
t2();
}
t1();
console.log(e);//6
console.log(window.d);//undefined
console.log(d);//报错,d is not defined
这里需要注意的是:
以window.xxx引用全局变量,寻找不到,作为某个属性不存在,返回undefined
直接以xxx引用某个变量,寻找不到,则是报xxx is not defined错误
3)关于作用域的应用,最常用的是判断输出的值是什么。在此之前,还需要涉及到一个词法分析过程。即在运行JS代码的过程中,有一个词法分析过程和执行过程。在判断时,也是有一定的套路的,以下是借鉴了别人的方法:
- 词法分析过程(分析3样东西)
- 分析函数声明
- 在分析变量声明
- 先分析参数
- 具体步骤:
- 第一步:函数运行前的一瞬间,生成Active Object(活动对象),以下称为AO
- 第二步:分析参数
- 1)把声明的参数,形成AO的属性,值全是undefined
- 2)接受参数
- 第三步:分析变量声明,如:var age,如果AO上还没有age属性,则添加AO属性,值为undefined,如果AO上已有age属性,则不做任何影响
- 第四步:分析函数声明,如:function foo(){},则把函数赋给AO.foo属性,如果此前foo属性已存在,则被无情覆盖
- 执行过程,按照从上到下的顺序执行
下面举个例子来详细说明:
function a(b){
alert(b);
function b(){
alert(b);
}
b();
}
a(1);
/*
词法分析过程:
0:生成一个AO={}
1:
1.1分析参数,AO={b:undefined}
1.2接收参数,AO={b:1}
2:分析变量,无,跳过
3:分析函数,有,AO={b:function(){alert(b)}}
执行过程:
79行:alert(b);//输出一个函数
83行调用,执行81行,输出一个函数
*/
如果函数里再嵌套函数,则继续按照上面的步骤继续分析
注意:对于函数的分析是从外到内,对于变量作用域的寻找是从里到外
在变量寻找的过程中,变量从包裹该变量的函数从内往外寻找的过程中,就形成了一条作用域链,一直沿着这条作用域链寻找,如果没有发现该变量,则值为返回值为undefined(该变量必须先声明)