趁着周末有时间,继续来与大家分享一下我对js作用域的理解。
作用域
域,表示的是一个范围,作用域就是作用范围。
简单点就是,作用域说明的是一个变量可以在什么地方被使用,什么地方不能被使用。
特别需要注意的是,ES5中没有块级作用域,唯一可以产生作用域的只有函数。
例:
for(var i=0; i<9; i++){
var num = i;
}
console.log(i); // 9
console.log(num); // 8
(若有块级作用域,上面代码会打印undefined)
词法作用域(js使用的)
所谓词法作用域(也叫静态作用域),就是定义在词法阶段的作用域,是由写代码时将变量和块作用域写在哪里来决定的,因此当词法分析器处理代码时会保持作用域不变。
可以理解为:js代码一旦写好,作用范围就已经确定好了。
Js中词法作用域规则:
- 函数允许访问函数外的数据
- 作用域规则首先使用变量提升分析
- 如果当前作用域中有该变量,就不再考虑外边的同名变量
和词法作用域相对的叫动态作用域,咱先看下面代码
var a = 123;
function f1 () {
console.log(a);
}
function f2 () {
var a = 456;
f1();
}
f2(); // 123
(若为动态作用域,以上代码会打印456)
由此可看出:
- 词法作用域是在代码书写或定义的时候确定,关注点是函数在何处声明。
- 动态作用域是在运行时确定的,关注点是函数在何处调用。
接下来介绍的就是执行环境,介绍它是为了引出作用域链这个概念。
执行环境
定义了变量或者函数有权访问的其他数据,决定了它们各自的行为。每个执行环境都有一个与之关联的变量对象(用来存储该执行环境的变量的。代码无法访问,解析器处理数据时会使用)。
值得一提的是使用var声明的变量会自动被添加到最接近的环境中。在函数内部,最接近的环境就是函数的局部环境。如果初始化变量时没有使用var声明,该变量会自动被添加到全局环境中。
每个函数都有自己的执行环境,当执行流进入一个函数时,函数环境就会被推入一个环境栈中,在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。
作用域链
Js高程是这么说的:
大致就是:
- 只要创建函数,就会创建一个作用域,而函数内部作用域可以访问函数外部作用域。
- 函数中可以创建一个函数,所以如果有多个函数嵌套,那么会构成一个函数内指向函数外的链式结构,这就是作用域链。
高程中更为详细的描述是在闭包那一章节:
举个例子:
function f1(){
function f2(){}
}
var num = 123;
function f3(){
function f4(){}
}
对应作用域链(简洁版):
访问原则:首先在局部环境中搜索是否存在该变量,不存在则向上一级作用域搜索,直到全局作用域中都未找到,则报错。f2可以访问f1的作用域,可以访问全局作用域。f3可以访问f4及全局作用域。
总结
掌握好了Js作用域,我们就可以在写代码时避免出现无意中创建了个全局变量、访问变量出错等一些低级的错误。同时,作用域链这块东西,其实就是闭包的原理,关于闭包,我会专门写一篇博客来详细讲解,敬请关注!