JavaScript中的闭包

这是篇文章主要是讲一下对闭包这一律念的理解。讨论闭包以前,咱们先从一个经典的例子提及es6

// 程序1
var arr = []
for(var i = 0; i < 3; i++){
    arr[i] = function () {
        console.log(i)
    }
}
arr[0]()  // 3
arr[1]()  // 3
arr[2]()  // 3
复制代码

你们都知道,这段代码最终输出都为3。由于函数调用的时候循环已经结束了因此 i 等于3,更为重要的是,es6以前没有块做用域,变量 i 的做用域不在for循环中,而在for循环以外。若是咱们想要看到输出结果依次为0,1,2,就得用到闭包了。不然,除非咱们能在每次循环的过程当中调用函数,由于只有在循环进行的过程当中 i 才会处于0,1,2的状态。好比像这样:bash

// 程序2
for(var i = 0; i < 3; i++){
    (function () {
        console.log(i)
    })()
}
// 0
// 1
// 2
复制代码

必需要在循环进行时调用,像下面这样都不行,由于这样和程序1实际上是同样的,函数调用时i已经等于3了。闭包

// 程序3
for(var i = 0; i < 3; i++){
    setTimeout(function () {
        console.log(i)
    }, 0)
}
// 3
// 3
// 3
复制代码

闭包主要就是用来解决这样的问题, 它让函数能够访问到函数所被建立时的上下文环境,不论这个函数在何时被调用。 因此闭包产生的条件有两个,一是函数能经过变量做用域规则访问到它被建立时的上下文环境,例如程序1,函数只是简单的访问了外部的变量 i,严格上讲不算闭包。二是函数在其它地方执行时,函数依然可以记住并访问到它所被定义时的上下文环境,咱们使用闭包来对程序1进行修改:函数

// 程序4
var arr = []
for(var i = 0; i < 3; i++){
    (function () {
        var j = i
        arr[i] = function () {
            console.log(j)
        }
    })()
}
arr[0]()  // 0
arr[1]()  // 1
arr[2]()  // 2
复制代码

不一样的是此次增长了当即执行函数并在里面定义变量 j ,咱们能够把当即执行函数称为 fn 。每次循环都会建立一个以当即执行函数为做用域的变量 j ,原来在程序1中函数访问的是外部变量 i ,如今访问的是fn这一闭包中的变量 j 。fn执行结束时 j 本应被回收,可是因为该做用域内还定义了一个内部访问了变量 j 的函数,该函数在将来可能被执行,因此 j 被“记住”了,也就是做用域链被保存了。咱们能够把fn称为一个闭包,闭包内能够定义函数而且这些函数能够访问闭包中定义的变量,例如:ui

function fn(){
    var a = 1;
    return function(){
        console.log(a)
    }
}
var module = fn()
module()  // 1
复制代码

fn返回的函数经过闭包可以访问到 a。spa

最后总结一下,闭包的主要做用在于“记住”函数定义是的某些外部变量,以供函数调用时能够访问。code

相关文章
相关标签/搜索