作用域(全局作用域/函数作用域)

220 阅读3分钟

一、作用域

1、什么是作用域(Scope)

作用域就是变量可以被调用生效的范围,js的作用域是定义时候产生的

2、JS的作用域的分类(含ES6)

JS作用域可以分为三大类:全局作用域 、局部作用域(函数作用域),块作用域

(一)全局作用域:

  直接编写在 script 标签之中的JS代码或者是一个单独的 JS 文件中的,都是全局作用域。

  全局作用域在页面打开时创建,页面关闭时销毁;

  在全局作用域中有一个全局对象 window(代表的是一个浏览器的窗口,由浏览器创建),在全局作用域中,所有创建的变量都会作为 window 对象的属性保存,可以直接使用。

var a;
let b;

(二)局部作用域(函数作用域):

  在函数内部就是局部作用域,这个代码的名字只在函数的内部起作用

  调用函数时创建函数作用域,函数执行完毕之后,函数作用域销毁;

  每调用一次函数就会创建一个新的函数作用域,它们之间是相互独立的。

function sun(x,y){
    return x+y
}

(三)块级作用域

块作用域由 { } 包括,if 语句和 for 语句里面的 { } 也属于块作用域。

function f1() {//块作用域
  let n = 5;
  if (true) {//块作用域
    let n = 10;
  }
  console.log(n); // 5
}

二、作用域链

只要是代码,就有一个作用域,函数会开辟一个新的作用域

当在函数作用域中操作一个变量的时候,会先在自身作用域中查找,如果有就直接使用,如果没有就向上级作用域中寻找。依次查找,一直到全局,全局还是没有,显示 is not defined。

根据内部函数可以访问可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问,就称为函数作用域链。

作用域链:内部函数访问外部函数的变量,采取的是链式查找的方法来决定取那个结构,这种结构称之为作用域链。

作用域链的原则:就近原则

作用域链采用链式查找的方式,一层一层向上查找,先查找外面的嵌套的函数是否有所需内容,找到就输出相应的结果,依次查找,一直到全局,全局还是没有,显示 is not defined。找到链式结构层次最低的就停止,就近原则.

实例:

下面代码最终输出的结果?

注意:在更长的结构中画图分析太过于麻烦,可以从输出目标console.log(); 位置向外层的结构看,寻找最近的变量。

var a = 1;
    function fn1(){
        var a=2;
        var b='22';
        fn2();
        function fn2(){
            var a =3;
            fn3();
            function fn3(){
                var a=4;
                console.log('a= ' + a);  //求 a的值
                console.log('b= ' + b);  //求 b的值
            }
        }
    }
    fn1();

最终结果是:

a= 4

b= 22

三、作用域的思考拓展

(一)闭包:

  闭包就是运用作用域链将变量传递于内部函数,后使用或者return暴露在内存中

(二)this指向:

  this指向是调用时决定的

  闭包情况下,this指向是调用时的上下文环境;就是其所在函数的作用域所属对象

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"></head><body></body></html>
<script>
  let obj = {
    getName: getName
  }
  function getName () {
    setTimeout(function xiaosan() {
      console.log(this === obj) // false
    }, 0)
  }
  obj.getName()
</script>

纳尼!!不是obj调用的吗?不是this会指向obj吗?究竟是谁?

别急别急,我们来分析一下,thisxiaosan函数。

但是xiaosan函数作用域环境是什么?xiaosan函数被定时器setTimeout调用,那定时器setTimeout的作用域是什么?哦,是不是恍然大悟,就是window对象