前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【入门级】从一道面试题了解js作用域及作用域链

【入门级】从一道面试题了解js作用域及作用域链

作者头像
十里青山
发布2023-03-16 19:53:15
3780
发布2023-03-16 19:53:15
举报
文章被收录于专栏:我的前端之路我的前端之路
theme: smartblue

昨天看了一道面试题,没想到竟然答错了,特别基础的问题还能答错,所以特别记录一下,因为考的是js的作用域的知识点,所以关于js的作用域也简单总结一下。

作用域【废话部分,有基础直接看面试题部分】

什么是作用域

啥是作用域呢,简单的说,就是变量可以生效的地方,就叫做作用域,也叫执行环境,大家也可以理解为变量可以发生作用的地方。举个栗子,我们国家颁布《刑法》,那么刑法就相当于一个变量,存储的值,就是刑法的内容,生效的范围就是作用域,也就是全国。

全局作用域

全局作用域就是代码运行时最外围的执行环境,比如在我国,最大的范围就是全国,全国就是全局作用域,而在js中,全局作用域被认为是window对象,而在上一篇文章中我们也说到了,在全局作用域中声明的变量为全局变量,而如果省略声明关键字声明的变量,默认也为全局变量。就像我们在国家的任何一个地方都需要遵从国家法律一样,我们在js的任何一个地方也都可以访问到全局作用域。

局部作用域

假如我们的国家是一个全局作用域,那它下面还有好多个省份和地区,在js 里也一样,全局作用域下面还有好多小的作用域,我们称之为局部作用域。在es6之前,js还不支持块作用域,所以在es6之前所谓的局部作用域就是指的函数作用域,也就是我们声明一个函数时这个函数内部的作用域。

1-1
1-1

如图所示,函数foo内部就是它所生成的局部作用域,变量bar就是这个局部作用域里的局部变量。

作用域嵌套与作用域链

上面我们说了,声明一个函数的同时就会创建属于它的函数作用域,那么函数可能会存在嵌套的情况,这时候就产生了作用域嵌套,这时候我们执行代码的话,就会产生一个作用域链,作用域链的前端,始终都是当前执行的代码距离最近的作用域。

1-2
1-2

如图所示就是一个嵌套的作用域,js解析变量的时候会遵循自下而上(自内而外)的规则沿着作用域链一级一级的查找,直到找到为止,如果查找到全局作用域(window)时依然没找到,就会报错。注意,作用域链是不可逆的,就是说我们在内层的作用域里可以访问外层作用域里的变量,但是在外层作用域里不能访问到内层作用域的变量。可以看下面的例子

代码语言:javascript
复制
var text = 'hello'
function foo() {
    var text1 = 'foo'
    function bar() {
        var text1 = 'bar'
        console.log(text1) // bar
        console.log(text) // hello
        console.log(text3) // text3 is not defined
    }
    bar()
}
foo()

上面的例子中,作用域链为 bar作用域 -> foo作用域 -> 全局作用域,打印text1的时候会先在最近的bar作用域中查找,如果可以找到就不会再往外找了,打印text的时候先在最近的bar作用域中查找,没找到就往foo作用域查找,还没找到,就往全局作用域查找,找到之后进行输出。打印text3的时候和前面一样,按照作用域链的顺序进行查找,都没找到,然后报错。由于作用域链是不可逆的,所以我们可以在bar作用域里访问全局作用域,但如果我们在全局作用域里打印text1,则会报错,因为全局作用域无法访问foo作用域。

另外要说一点,在我们讲this的那一篇文章中说了,this是在函数调用时决定的,在函数被定义时并没有this。而作用域则刚好相反,作用域是在函数定义时决定的,跟函数在哪里被调用没有关系。所以无论我们在哪里调用函数,都不会改变他的作用域链。

块作用域

上面我们说了,在es6之前,js中是没有块作用域的,在es6中,添加了let关键字实现了对块级作用域的支持。那么什么是块级作用域呢,其实就是两个大括号包裹的作用域。而且在我们日常的代码中非常常见,比如if语句后跟的大括号,for循环后跟的大括号。那有的同学会说,这不是有块级作用域吗,那为什么又说没有块级作用域呢?我们又怎么区分有没有块级作用域呢?其实很简单,我们来看看代码就知道了。

代码语言:javascript
复制
if (true) {
    var test = 'hello'
}
console.log(test) // hello

看,我们在大括号外也访问到了大括号里面的变量,上面我们说了,在局部作用域(块级作用域也属于局部作用域)外面是访问不到作用域里面的变量的,所以这里的‘块作用域’其实并没有真正的形成作用域,只不过是徒有其表罢了,这样子的危害就是容易污染全局作用域,而且容易给我们造成一定程度上的误解。比如下面这样子。

代码语言:javascript
复制
var index = 5
for (var index = 0; index < 10; index++) {
    /**/
}
console.log(index) // 10

很明显,上面代码中for循环中的index污染了全局作用域中的index,如果我们不小心的话很容易造成意想不到的后果,当然我们也可以尽量小心的去给变量命名,细心的检查代码,或者使用try...catch(感兴趣可以去搜一下,或者看js高级程序设计)去实现块作用域,以便代码如我们想象般的运行,可那样就会花费更多的精力,好在es6推出了let关键字,从代码层面支持了块作用域,减少了我们很多的工作量,来看看let的效果

代码语言:javascript
复制
var index = 5
for (let index = 0; index < 10; index++) {
    /**/
}
console.log(index) // 5

看,代码完全按照我们想象那样执行,let声明的变量支持块作用域,仅在块作用域内可访问,不会影响全局变量。

相关面试题

下面我们就来看看很经典的一道面试题

代码语言:javascript
复制
for (var index = 0; index < 6; index++) {
    setTimeout(function(){
        console.log(index)
    })
}

知道了块作用域再理解这道题就很简单了吧,因为这里用的是var关键字,所以这里没有块作用域,也就是说这里的index其实是一个全局变量,然后每次对index进行++的操作其实都是操作的同一个变量——全局变量index,然后我们里面又用的是setTimeout,一个异步函数,虽然我们这里没有设置定时时间,但它还是一个异步函数,需要等到for循环全部结束后才会运行,这时候index就已经是6了,所以会打印出来6个6。那如果我们把var换成let呢?

代码语言:javascript
复制
for (let index = 0; index < 6; index++) {
    setTimeout(function(){
        console.log(index)
    })
}

打印结果:0 1 2 3 4 5

完全符合我们的预期,这里我们使用的是letlet声明的变量支持块作用域,也就是仅在当前作用域内有效,所以这里我们循环中的每一个setTimeout引用的index其实都是单独的变量,互不影响。

很多人都知道,上面的问题除了用let还有另一种方法可以解决,就是立即执行函数

代码语言:javascript
复制
for (var i = 0; i < 5; i++) {
    (function (i) {
        setTimeout(function () {
            console.log(i)
        })
    })(i)
}

打印结果:0 1 2 3 4 5

这样子也可以符合预期,但如果我们把这道题再改一下呢

代码语言:javascript
复制
for (var i = 0; i < 5; i++) {
    (function (i) {
        setTimeout(function (i) {
            console.log(i)
        })
    })(i)
}

这时候会打印什么呢,答案是5个 undefined,我最开始也有点懵,为什么呢,但仔细一看其实很简单,因为setTimeout里面那个未命名函数也有自己的作用域,它接收一个参数i,其实就是在自己的作用域里定义了一个空的变量i,所以打印的时候在当前作用域里可以找到变量i,它就不会再继续往外找了,又因为找到的变量i是空值,所以会是undefined。

近期找工作比较困难,我又比较菜,也没统招学历,如果有北京上海要求比较低的公司可以内推欢迎私信我,外包也行,我四年经验,技术栈vue

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-02-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 作用域【废话部分,有基础直接看面试题部分】
    • 什么是作用域
      • 全局作用域
        • 局部作用域
          • 作用域嵌套与作用域链
            • 块作用域
            • 相关面试题
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档