谈谈Js作用域

趁着周末有时间,继续来与大家分享一下我对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作用域,我们就可以在写代码时避免出现无意中创建了个全局变量、访问变量出错等一些低级的错误。同时,作用域链这块东西,其实就是闭包的原理,关于闭包,我会专门写一篇博客来详细讲解,敬请关注!