JS进击之路:闭包

引言

闭包这个词对不少前端开发人员来讲既熟悉又陌生,熟悉是由于不少人都用过闭包,可是用的时候不知道闭包,陌生是由于并不理解闭包,接下来这篇文章将会从多方面介绍闭包前端

定义

闭包是怎么定义的呢?当函数能够记住并访问所在的词法做用域时,就产生了闭包,即便函数在当前词法做用域以外执行。来看一个具体例子:面试

function foo () {
  var a = 2
  function bar () {
    console.log(a)
  }
  return bar
}
var baz = foo()
baz() //2

函数bar的词法做用域能够访问foo的内部做用域,而且bar在被做为返回值赋值给baz执行时,bar函数在定义时的词法做用域之外的地方被调用,依然能够访问foo函数的内部做用域变量a,这就是闭包闭包

分析

如今让咱们来看为何闭包能够在定义的词法做用域外记住而且访问定义时的词法做用域的变量,想要一探究竟,先来看一个简单的例子来函数的执行过程:函数

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


上面是一个简单的函数调用,以及在执行时的上下文环境,重点看执行时上下文环境,在建立foo函数时,会建立一个预先包含全局变量对象的做用域链,这个做用域链被保存在内部的[[Scope]]属性中,当调用foo()函数时,会为函数建立一个执行环境,而后经过复制函数的[[Scope]属性中的对象构建起执行环境的做用域链。此后,又有一个活动对象(包含this、arguments、a)被建立并被推入执行环境做用域链的前端,对于foo函数来讲,其做用域链包含两个变量对象,一个时全局的变量对象,一个是局部的活动变量对象,通常来讲当函数执行完成后,局部的活动变量对象会被销毁,只留全局的,可是闭包执行过程有所不一样,来看具体例子:this

function foo () {
  var a = 2
  function bar (b) {
    console.log(a + b)
  }
  return bar
}
var baz = foo()
baz(3) //5


接下来来分析下上面闭包的执行上下环境,在一个函数内部定义的函数会将包含函数的活动对象添加到它的做用域链中,所以,bar函数的做用域链中会包含foo函数的活动对象,在bar函数从foo中被返回后,它的做用域链条被初始化为全局变量和foo中活动对象,所以,bar函数能够访问foo函数中定义的全部变量,同时foo函数在执行完毕后,其活动对象也不会被销毁,由于bar函数的做用域链仍然在引用这个活动对象。spa

常见问题

说到闭包相关的问题,最典型的就是变量和this指向这两类问题。code

变量

function test () {
  var result = new Array()
  for (var i = 0; i < 6; i++) {
    result[i] = function () {
      return i
    }
  }
  return result
}


上面的代码展现就是面试题里面常常会碰到,result的结果从上面截图能看到,做用域中保存的i都是6,这是为何呢?由于闭包保存的是函数中的活动对象,所以它们引用的都是同一个变量,而且是变量的最后一个值,所以都是6,那这个问题怎么解决呢?最多见的最简单确定是将var换成let,也能够像下面这样:对象

function test () {
  var result = new Array()
  for (var i = 0; i < 6; i++) {
    result[i] = (function () {
      return i
    })()
  }
  return result
}

将闭包直接改为一个自执行函数,自执行函数自己是没有变量做用域的,所以会使用外层函数的变量做用域,这样也能达到咱们想要的效果作用域

this指向

var name = "window"
var obj = {
  name: "object",
  getName: function () {
    return function () {
      return this.name
    }
  }
}
console.log(obj.getName()())

上面这段js代码的this.name的返回值是window,这是为何呢?按照上面写到的,此匿名函数在执行过程当中,它的做用域会包含三部分:自身的活动对象、getName函数的活动对象和全局的变量对象,同时每一个活动对象自动取得两个特殊的变量:this和arguments,可是内部函数在查找this时是没法直接访问外部函数的this变量,所以会沿着做用域链去查找全局变量中继续查找,若是想要取外部函数中的this取值也很简单,只须要向下面代码这样:开发

var name = "window"
var obj = {
  name: "object",
  getName: function () {
    var that = this
    return function () {
      return that.name
    }
  }
}
console.log(obj.getName()())

将this赋值给一个变量,内部函数是能够访问外部函数变量的,这样就解决了

总结

闭包是一个容易混淆不清的概念,这篇文章对闭包的定义、执行、常见问题作了简单的介绍,但愿经过这篇能对你们理解和使用闭包有所帮助。若是有错误或不严谨的地方,欢迎批评指正,若是喜欢,欢迎点赞。

相关文章
相关标签/搜索