闭包

定义:当函数可以记住并访问所在的词法作用域时,就产生了闭包 (你不知道的JavaScript)
闭包是指有权访问另一个函数作用域中的变量的函数(JavaScript高级程序设计)

特点

  让外部访问函数内部变量成为可能;

  局部变量会常驻在内存中;

  可以避免使用全局变量,防止全局变量污染;

  会造成内存泄漏(有一块内存空间被长期占用,而不被释放)

DEMO:

function foo() {
    var a = 2;
    return function fun1() {
        console.log(a)
    }
}
var fun2 = foo()
fun2()  // 2

 在上面的例子中,fun1能够访问foo的内部作用域,我们把fun1作为一个值返回。在foo()执行后,把foo()的返回值 fun1 赋值给fun2并调用fun2。打印出了结果2.

此时,我们可以说fun1记住并访问了所在的词法作用域 或者说 fun2访问了另一个函数作用域中的变量(fun2在全局作用域中声明,访问了foo的内部作用域)
由于引擎有自动的垃圾回收机制,在foo()执行后(不再使用),通常foo的整个内部作用域会被销毁,对内存进行回收。闭包的神奇之处正是可以阻止这件事情的发生,因为fun1依然持有对该作用域的引用,这个引用就叫做闭包。
无论使用何种方式对函数类型的值进行传递,当函数在别处调用时,都可以看到闭包。

DEMO2

function add(x){
    return function(y){
      console.log(x,y)
      return x + y;
    };
  }
  var addFun1 = add(2);

  var addFun2 = add(9);
  console.log(addFun1)
  //  ƒ (y){
  //    console.log(x,y)
  //    return x + y;
  //  }
  console.log(addFun2)
//  ƒ (y){
//    console.log(x,y)
//    return x + y;
//  }
  console.log(addFun1(2))//2、2、4

  console.log(addFun2(2))//9、2、11

直接传递

function foo() {
    var a = 2;
    function baz() {
        console.log(a);
    }
    bar(baz); //对函数类型进行值得传递
}
function bar(fn) {
    fn(); // 闭包产生了
}

或者间接的传递

var fn;
function foo(){
    var a = 2;
    function baz() {
        console.log(a);
    }
    fn = baz; //对函数类型进行值得传递
}
function bar(fn) {
    fn(); //闭包产生了
}
foo()
bar()

无论通过什么手段将内部函数传递到所在的词法作用域之外,它都会保持对定义时作用域的引用,这个函数无论在何处执行,都产生了闭包。

现在你理解闭包了吗?思考一下你的代码中产生了哪些闭包?

无处不在的闭包

定时器

function wait(message) {
    setTimeout(function timer() {
        console.log(message)
    }, 1000)
}
wait('hello 闭包')

循环和闭包

思考下面的代码

for (var i=1;i<=5;i++){
    (function(j){
        setTimeout(function timer() {
            console.log(j)
        },j*1000)
    })(i)
}

正常情况下,我们对这段代码的预期是每隔一秒输出一个数字,1-5。但是实际上并不会这样,这段代码会每间隔一秒输出一个数字6。这是为什么?

首先6从哪里来,当timer执行时,for循环早已结束,终止条件是i=6。
有些小伙伴可能会不明白了,timer不是创建了闭包,保持了对i的引用吗?
没错。for循环了5次,创建了5个闭包,但是都是保持了对同一个i的引用(i是全局变量)。所以当timer执行时,i = 6,输出6,没毛病。
那么如何达到我们想要的效果,我们需要更多的闭包。循环过程中每个迭代都需要一个闭包

for (var i=1;i<=5;i++){
    (function(j){
        setTimeout(function timer() {
            console.log(j)
        },j*1000)
    })(i)
}

立即执行函数创建了一个新的作用域,使得延迟函数的的回调可以将新的作用域封闭在每个迭代内部。问题解决。

 

原文地址:https://www.cnblogs.com/hy96/p/12973471.html