相信很多学习前端的朋友都认识JavaScript的三座大山。
这里假设阅读的朋友对js已经有一定的基础。如果没有的话,请暂时留步。
那js三座大山是什么呢?
1、原型和原型链
2、作用域和闭包
3、异步和单线程
作用域和闭包基本上是每个学习前端的必经之路,也是必须聊的一个话题。
那么接下来就来聊聊作用域和闭包
那就问题来了
第一个问题:什么是作用域?作用域分为什么?
作用域分为全局作用域和局部作用域,这里只讨论ES6之前的,因为ES6有了块级作用域概念了。说实话作用域可以延伸出来其他的概念,比如作用域链、预处理机制。提到的话我会说明一下。现在就以作用域和闭包这两大主体作为线索展开。
全局作用域:定义在window对象下,在window定义的变量可以被局部作用域使用。
局部作用域:定义在函数内部。声明的变量,只能在函数内部使用。
来看看几个例子就明白,这个还是比较简单的。
第一个例子证明:在全局作用域下无法访问局部作用域
思考一个问题:为什么函数内部打印的结果的未定义?第二个例子证明:在局部作用域的变量可以访问全局作用域的变量
思考:函数内部打印结果是什么?为什么?函数外部打印的结果为什么为2?第三个例子证明:有var声明的才是变量,不然在函数内部是改变参数值。
小帖士:下面分别是两个例子,需要分开执行。
思考:对比两个例子,为什么最后打印的都是1?为什么第一次函数内部打印的结果是未定义?先埋一下伏笔哈!在文章最后我都会一一解答,这里是想让你思考这些问题,让你们自己思考出来印象会更深刻。不会也没关系,请听我娓娓到来。或者如果实在想知道答案,直接拉到最后。
接下来有请我们今天另外一位嘉宾:闭包先生
闭包先生是我们JavaScript的一员,也是贡献比较多的一员,相传他有一门绝学:定义在函数内部的函数,能够使函数内部和外部进行链接。
引用权威的一句话:JavaScript中的函数在定义他们的作用域运行,而不是在执行它们的作用域里运行。
其他博客这么说:指有权访问另一个函数作用域的变量的函数。
是不是一头雾水?哈哈!刚开始接触我也是一头雾水,那就用实例说话
第一个问题:闭包是什么已经给出答案了。例子的话等会跟下面一个问题一起给。
第二个问题:闭包的优缺点?
思考:闭包的优点有哪些?缺点先放一边来讲一下这个过程吧!我们知道fn1函数执行之后,返回的是fn2函数名,为了调用fn2函数的话需要加一个()。而每次执行完函数fn2之后,变量都会加1,并且会存储在fn1函数的局部作用域中。先前说的什么是闭包这个问题,就是定义在函数里面的函数,在外部能够访问函数内部的变量,但是我们知道访问变量只沿着作用域链向上查找,不能向下。所在可以在函数内部定义一个函数,将变量传到定义的函数里面(指fn2),再通过return函数名的方式将fn1函数里面的变量访问到。
上面说到作用域链也随便说一下,不深入剖析。
什么是作用域链?
上面的例子就可以说明:fn2的局部作用域---->fn1的局部作用域--->window全局作用域
在这里我给你抛出一个问题:能不能改变作用域链的大小?(这篇文章就不说了)
接下来就解决之前给你们抛出去的几个问题,想到了想不到看我的见解和你的讲解是否有分歧?
问题一:为什么函数内部打印的结果的未定义?
这个就要请出我们的导演啦!(js预解析机制)
很多刚学习js的同学都会有疑惑,为什么不是打印不是1?我们js有他的运行机制,先预解析,再逐行运行代码。
那么问题来了,预解析过程在做什么?
原来我们的js中,预解析是提升声明变量,怎么个提升法?
提升函数里面的变量们就变成这个样子了。将var a;提到函数顶部,所以访问不到全部变量问题二:函数内部打印结果是什么?为什么?函数外部打印的结果为什么为2?
打印结果是1,原因是在函数内部打印的时候,函数内部还没有这个变量,因为也没有存在变量提升的情况,所以会向全局变量伸出他的魔爪。找到全局变量a= 1;函数内部还是会继续执行,将a赋值为2之后,再函数外部打印自然也就是2啦!
问题三:对比两个例子,为什么最后打印的都是1?为什么第一次函数内部打印的结果是未定义?
就是有参数的情况下,函数内部和参数一样的变量名都代表参数。
问题四:闭包的优缺点
优点:有利于封装函数,能够访问局部变量
缺点:占用内存和内存泄漏问题
好了,就到这里了,有问题的欢迎下方留言。一起学习,有一句话想了好久,在我遇到瓶颈的时候就会告诉自己:“别人也会遇到,如果我解决了,我才能追上他们。“
网友评论