JavaScript基础—作用域与作用域链

在函数部分提到:ES6之前,JavaScript只有全局作用域,函数作用域,没有块作用域({}作用域),例如:

if(true){
  var a = 3
}
console.log(a) // 3

for(var i=1;i<4;i++){
  //...
}
console.log(i) //4

而在Java语言中,是有块作用域的哦,这也是JavaScript与Java语言的一个区别。下面我们进一步的来了解JavaScript作用域的相关知识。

一、作用域

,通俗的理解就是一块“地盘”,从字面理解,它是一个静态的概念。

同样的,作用域呢,就是一个代码段所在的区域,编写时所在的区域,它也是一个静态的概念。

在ES6之前,除了全局作用域,只有函数才能创建作用域(创建的作用域叫做函数作用域)。

其实作用域是一个很抽象的概念,我们看下面的代码:

var a=10,b=20

function fn(x){
  var a = 100,c = 300;
  console.log('fn:',a,b,c,x)

  function bar(x){
    var a = 1000,d = 400;
    console.log('bar:',a,b,c,d,x)
  }
  bar(5)
}

它产生了几个作用域呢?根据上面的描述,上述代码产生了三个作用域:

图1

综合上述的描述和实例代码,我们可以得到一个结论:作用域的个数遵循“n+1”原则,“n”表示定义的函数个数,“1”表示全局作用域

当我们调用fn函数的时候,输出如下:

图2

我们不难发现:

fn输出的数据:a来自fn作用域;b来自全局作用域;c来自fn作用域;x来自函数传参;

bar输出的数据:a来自bar作用域;b来自全局作用域;c来自fn作用域;d来自bar作用域;x来自函数传参;

那么这么变量的值是怎么查找的呢?是根据什么来找的呢,那就是我们的作用域链。

二、作用域链

如图1所示,产生了三个作用域:全局、fn作用域、bar作用域。全局包含fn,fn又包含了bar,分别形成了上下级关系。

  2.1 作用域链的在变量查找中的体现

下面我们来描述一个变量的查找过程:bar中输出c时的查找过程

  1. 第一步:在当前作用域(bar作用域)查找变量c,没找到,那么向上一级作用域查找;

  2. 第二部:在bar的上一级作用域(fn作用域)中找,找到了,输出

大体上就是这样的一个查找过程。进一步总结:

第一步,现在当前作用域查找a,如果有则获取并结束。如果没有则继续;

第二步,如果当前作用域是全局作用域,则证明a未定义,结束;否则继续;

第三步,(不是全局作用域,那就是函数作用域)将创建该函数的作用域作为当前作用域;

第四步,跳转到第一步。

这样一级一级在作用域中查找变量的过程,形成了一个链路,这个链路就叫做作用域链。当前,作用域链在代码写下的时候就形成了,并非是在查找的时候才形成。

  2.2 作用域的作用

作用域做大的作用就是隔离变量,不同作用域下的同名变量不会形成冲突。

 2.3 歧义说法

经常会听到这样的说法:“当前作用域没有的时候,去它的父作用域找”。

这一句话,在理解上一不小心,就会被绕进去,就会产生歧义的。例如:

var a=10

function fn(){
  console.log(a)
}

// fn2的入参f是个函数
function fn2(f){
  var a = 200;
  f();
}

fn2(fn);
//控制台输出多少呢?输出10,而不是200

所以,更贴切的说法是:要到创建这个函数的那个代码段所对应的作用域中去查找,而不是调用。这也更进一步印证了,作用域是个静态的概念

三、总结

  • JavaScript在es6之前,没有块级作用域;

  • 作用域是个静态的概念,作用域链也是个静态的链路;

  • 作用域的个数遵循“n+1”原则:n表示定义的函数个数,1表示全局作用域;

  • 作用域的作用,隔离变量,不同作用域下的同名变量不会形成冲突;

  • 作用域是在函数创建的时候就产生了,一直存在,不会因为调用的场景不同而改变

  • 注意“到父作用域中查找”的理解,避免产生歧义

以上是一些作用域的一些相关知识,希望能对你有所帮助。欢迎关注同步微信公众号:前端小菜的进阶之路

公众号:Mr-Small_Teem

猜你喜欢

转载自blog.csdn.net/jingsi1991/article/details/84726920