js 作用域,作用域链,闭包

什么是作用域?


       作用域是一种规则,在代码编译阶段就确定了,规定了变量与函数的可被访问的范围。全局变量拥有全局作用域,局部变量则拥有局部作用域。 js是一种没有块级作用域的语言(包括if、for等语句的花括号代码块或者单独的花括号代码块都不能形成一个局部作用域),所以js的局部作用域的形成有且只有函数的花括号内定义的代码块形成的,既函数作用域。

什么是作用域链?


       作用域链是作用域规则的实现,通过作用域链的实现,变量在它的作用域内可被访问,函数在它的作用域内可被调用。

作用域链是一个只能单向访问的链表,这个链表上的每个节点就是执行上下文的变量对象(代码执行时就是活动对象),单向链表的头部(可被第一个访问的节点)始终都是当前正在被调用执行的函数的变量对象(活动对象),尾部始终是全局活动对象。

作用域链的形成?


       我们从一段代码的执行来看作用域链的形成过程。

  function fun01 () {
      console.log('i am fun01...');
      fun02();
  }
  
  function fun02 () {
      console.log('i am fun02...');
  }
 fun01(); 

数据访问流程


       如上图,当程序访问一个变量时,按照作用域链的单向访问特性,首先在头节点的AO中查找,没有则到下一节点的AO查找,最多查找到尾节点(global AO)。在这个过程中找到了就找到了,没找到就报错undefined。

关于闭包


1.什么是闭包?

       函数对象可以通过作用域链相互关联起来,函数体内的数据(变量和函数声明)都可以保存在函数作用域内,这种特性在计算机科学文献中被称为“闭包”。既函数体内的数据被隐藏于作用于链内,看起来像是函数将数据“包裹”了起来。从技术角度来说,js的函数都是闭包:函数都是对象,都关联到作用域链,函数内数据都被保存在函数作用域内。

  //闭包实例
  function outerFun () {
      var outerV1 = 10
      function outerF1 () {
          console.log('I am outerF1...')
      }
  
      function innerFun () {
          var innerV1 = outerV1
         outerF1()
     }
     return innerFun   //return回innerFun()内部函数
 }
 var fn = outerFun()        //接到return回的innerFun()函数
 fn()                    //执行接到的内部函数innerFun()

此时它的作用域链是这样的:

3.闭包的好处及使用场景

       js的垃圾回收机制可以粗略的概括为:如果当前执行上下文执行完毕,且上下文内的数据没有其他引用,则执行上下文pop出call stack,其内数据等待被垃圾回收。而当我们在其他执行上下文通过闭包对执行完的上下文内数据仍然进行引用时,那么被引用的数据则不会被垃圾回收。就像上面代码中的outerV1,放我们在全局上下文通过调用innerFun()仍然访问引用outerV1时,那么outerFun执行完毕后,outerV1也不会被垃圾回收,而是保存在内存中。另外,outerV1看起来像不像一个outerFun的私有内部变量呢?除了innerFun()外,我们无法随意访问outerV1。所以,综上所述,这样闭包的使用情景可以总结为:

(1)进行变量持久化。

(2)使函数对象内有更好的封装性,内部数据私有化。

function countFun () {
    var count = 0
    return function(){
        return count++
    }
}

var a = countFun()
a()//0
a()//1,变量count没有被回收

4.闭包的注意事项

       由于闭包中的变量不会像其他正常变量那种被垃圾回收,而是一直存在内存中,所以大量使用闭包可能会造成性能问题。

参考:http://www.cnblogs.com/ivehd/p/scopechain.html

原文地址:https://www.cnblogs.com/dehuachenyunfei/p/6597193.html