最近除了尝试一些新的技术以外,更喜欢把时间花在研究JS的基础知识上,让我对JS很多底层实现的理解加深了不少,今天闲来无事就和各位兄台交流一下,说说我自己对JS中作用域,作用域链和闭包的理解吧,如有不对的地方,希望各位兄台多多包涵,欢迎留言纠正。
作用域:
首先来聊聊作用域,其实在我个人理解中作用域更像是JS中的一种规则,在代码编译的时候就已经确定了该变量是属于什么变量并且拥有什么样的作用域了,简而言之就是规定了能被访问的范围。在只有var定义变量的时代,JS是不存在块级作用域的,只分全局作用域和局部作用域。
全局作用域:
全局变量拥有全局作用域,从定义变量的位置来看,可以简单理解为不在任何函数内部,也可以说是定义在最外层函数的外面,在定义后可以在任何地方访问到该变量。
局部作用域:
局部变量拥有局部作用域,只在固定的代码片段中可以访问到,一般是在函数内部定义,所以也可以叫函数作用域。在这里需要提一下,在函数内部不使用var定义而是直接赋值操作的话,生成的并不一定会是局部变量,它更像是在对一个属性赋值操作,首先会在当前作用域链中查找该变量,如果有就会对该变量进行赋值操作,如果没有就会在全局对象中创造一个同名的属性并且赋值,也就是说函数内部直接给一个未定义的变量赋值,并且该变量还不在当前作用域内可以找到的话,就会是一个全局变量。当然这样声明出来的全局变量和在全局作用域中使用var声明出来的全局变量还是有一些差别的,例如前者默认是可配置的,后者默认是不可配置的。在这里还有一点比较特殊,只要在函数内定义了一个局部变量,在该函数被解析的时候是会将这个变量提前声明的,也就是说在一个函数内部不管变量在哪一行定义,它的声明都将被放到函数内部的最开始处,当然什么时候赋值还是得看变量在哪一行赋值的才行,在赋值之前都会被undefined替代但并不会报错。
作用域链:
什么是作用域链呢?我的理解就是,如果在一个函数内部声明一个变量和一个函数,内部的函数可以访问到外部函数的变量这种规则或者说是这种机制,就是作用域链,用链式的方法单向的一层一层往外查找。想要具体了解这个规则,我们得先了解一下JS的执行环境。
执行环境:
执行环境也可以叫作执行上下文,当JS解释器初始化执行代码时,它首先默认进入全局执行环境,从此刻开始,函数的每次调用都会创建一个新的执行环境,那怕同一个函数被调用多次,执行环境也会创建多个,并且每个执行环境关联一个变量对象也可以称作活动对象,在此环境中定义的变量和函数都会被保存到这个变量对象中。当一个函数被调用时,该函数关联的变量对象就会被放入一个环境栈中,在该函数执行完之后,对应的变量对象将被弹出,把控制权交给之前的执行环境变量对象。
大概讲述了一下执行环境后在回过来讲述一下作用域链。
当我们第一次调用一个函数的时候,会创建执行环境以及变量对象的一个作用域链,并把作用域链赋值给了一个内部的属性([scope])。作用域链主要是保证在执行中有序的去访问变量或者函数,在作用域链中包含了环境栈中的每一个执行环境关联的变量对象,由此可以决定变量是否在访问范围内,全局对象始终都会是作用域链的最后一个对象,当前执行的活动对象才会是第0位对象。访问一个变量时,是沿着作用域链一级一级的搜索,从第0位开始逐步往后搜索,直到找到标识符为止,所以外部环境是不可以直接访问内部环境的变量和函数的,但是内部环境却是可以访问到外部环境的变量和函数的,这里的内部环境就是处于环境栈中第0位的活动对象。
闭包:
在刚开始接触JS的很长一段时间中,我对闭包的概念一直很模糊,工作中也很少用到,但是其实闭包的作用很强大。当一个执行环境中所有代码执行完毕后,该变量对象应该被弹出环境栈,随即销毁,内部的变量和函数也应该随之消失,但是当调用闭包时,会生成一个全新的执行环境和作用域链,虽然闭包的外部函数已经执行结束了,环境被销毁了,但是其关联的活动对象在闭包的作用域链中被引用,所以并不会被销毁而是会一直存在内存中。 其实说了这么多,可能有点复杂,我简单概括为两点:
第一点是可以读取到将闭包嵌套在内部的函数的变量,无论在闭包外嵌套了多少层函数,这些函数的变量都可以在闭包内部访问到(用作用域链搜索)。
第二点是让这些外部变量始终都保存在内存中,提高执行速度,因为这一点所以使用闭包也要特别注意,如果滥用闭包很容易引起内存泄漏。我对于闭包的使用主要集中在这几点,例如需要处理一个过程比较复杂耗时很长的函数,每次重复调用都会花很长时间,那我会使用闭包将这个结果储存起来,提高执行速度;还有例如想要访问外部函数中的变量或者参数,即使是在执行完毕以及被销毁之后,可以使用闭包来返回这些本该被销毁的变量和参数。
结尾:
今天就说这么多,打了这么多字好累啊,都是我自己的理解结合工作上使用的一些经验之谈,如有不妥之处随时欢迎指正,下一次我们再来聊聊JS中原型和原型链是个啥,仅仅是为了定义一些公用的方法吗,似乎没有想象中这么简单哦?