本文已参与「新人创作礼」活动,一起开启掘金创作之路。
前言
刚刚跟同事聊天中,无意中问到闭包的问题。在畅谈了一段时间之后,发现要掌握闭包知识,前置条件也不能少啊。我们的作用域和作用域链还是要好好谈谈的。
作用域 & 作用域链
作用域(scope)
- 浅显的理解: 作用域就是变量的可用范围(scope)
- 为什么要有作用域: 目的是防止不同范围的变量之间互相干扰。 例:
var x=1;
function f(){
var y=2;
function z(){
var z=3;
}
}
js中包括2级作用域
- 全局作用域
- 不属于任何函数的外部范围称为全局作用域。
- 保存在全局作用域的变量称为全局变量-----如上诉代码中的 x
全局变量的特点
- 优点: 可反复使用
- 缺点: 全局污染——开发时禁止使用
2. 函数作用域
- 一个函数内的范围称为函数作用域
- 保存在函数作用域内的变量称为局部变量
特例: 形参变量也是函数内的局部变量
- 形参变量虽然没有用var声明
- 但是形参变量也是函数内的局部变量! 例:
var x=1;
function f(y){ //y--函数内的局部变量
function z(){
var z=3;
}
}
局部变量的特点
- 优点: 不会被污染
- 缺点: 无法反复使用
非常重要: 只有函数的{},才能形成作用域
- 不是所有{}都能形成作用域。
- 也不是所有{}内的数据都能是局部变量
- 比如:
- 对象的{},就不是作用域!
- 对象中的属性,也不是局部变量
- 除函数{}之外的其余{},都不是作用域。
- 都拦不住内部的变量超出{}的范围影响外部程序 例:
console.log(a);//undefined
if(false){
var a=10;
}
console.log(a);//undefined
作用域链(scopes / scope chain)
其实每个函数在定义时,就已经规划好了自己专属的一个查找变量的路线图,称为作用域链 比如:
var x=1;
function f(){
var y=2;
function z(){
var z=3;
}
}
当我们定义函数z()时,函数z就为自己规划好了一个由内向外的查找路线。 以防未来运行时,一旦自己缺少变量,应该去找谁——未雨绸缪。
- 一个函数可用的所有作用域串联起来,就行成了当前函数的作用域链。
特殊: 给从未声明过的变量赋值(不报错,而是自动在全局创建变量)
总结
Js中只有两种局部变量:
- 函数内var出来的
- 函数的形参变量
看不见var,形参里也没有,就不是局部变量。
强调
形参变量也是函数的局部变量,函数传参采用的是按值传递。原始类型的值,在传参时,是将原变量的值复制一个副本给函数形参变量。所以,在函数内,修改形参变量,不影响外部原变量的值。 看代码理解一下:
var a=10;
function fun(a){
a++;
console.log(a);
}
fun(a); // ?
console.log(a);
作用域的本质
JS中,作用域和作用域链都是对象结构
总结: 全局作用域其实是一个名为window的对象,所有全局变量和全局函数都是window对象的成员。
总结: 函数作用域其实是js引擎在调用函数时才临时创建的一个作用域对象。其中保存函数的局部变量。而函数调用完,函数作用域对象就释放了。
所以:JS中函数作用域对象,还有个别名——”活动的对象(Actived Object)”简称, AO。所以,局部变量不可重用。
举个例子:
var a=10;
//new Function()
function fun(){
var a=100; //临时创建 函数作用域对象地址
a++; //按照顺序进行执行代码
console.log(a);
}
fun(); //创建地址----new Function()
//释放函数、作用域对象,局部变量紧跟着释放,所以局部变量在函数调用后就不存在了
console.log(a);