关于闭包的几点认识

什么是闭包?

闭包一般人都说, 是函数中定义的一个函数,有的人也说函数中套函数。 

其实准确点说,闭包是一个能够访问到其他函数内部变量的函数,

当然这个其他函数不是父子关系的函数, 而是兄弟关系的函数。


举个最简单的例子引导一下

function alwaysLinger(){
    var num = 0;

// 一堆一堆逻辑 }
console.log( num ); // num is not defined;

 

在外面访问 num 肯定是访问不到,还会报错:num is not defined;
但是你又想访问到 alwaysLinger 函数内部的局部变量。
这是时候你就需要闭包了。

function alwaysLinger(){
    var num = 0;
    // 一堆一堆逻辑
    return function(){
      return num;
    }
}
console.log(  alwaysLinger()() );  //  0;

 

看起来,好像也没有什么神奇之处,当你调用alwaysLinger函数后,返回一个函数,执行返回的函数,再次返回 num 变量。

这样就取了函数内部的值。看到这里你可能你会迷惑, 为什么要返回一个函数,然后再返回num呢,直接返回num不好吗?

我建议你带着这个疑问看下去。


闭包其实是一种现象:
父函数内的子函数,被父函数之外的变量引用,父函数里的变量永远不会销毁(也说明了js的垃圾回收机制不会生效,父函数中的变量永远存在)

下面举几个例子,简单的说明闭包常见的运用场景。

1、使变量始终存在内存中

function alwaysLinger(){
  var a = 0;
  return function(){
    return a++;
  }
}

console.log( alwaysLinger()() ); // 0
console.log( alwaysLinger()() ); // 0
console.log( alwaysLinger()() ); // 0
console.log( alwaysLinger()() ); // 0

var func = alwaysLinger();

console.log( func() ); // 1
console.log( func() ); // 2
console.log( func() ); // 3
console.log( func() ); // 4
console.log( func() ); // 5

 

前几个console,输出同一个值,是因为每次执行都返回一个新值,
后几个console,输出递增的值,是因为每次执行后操作的都是一个值,就是函数中的局部变量num。我们使用闭包不单单只是为了取到局部变量,还要去操纵它。


2、模拟块级作用域

比如,我们现在要实现点击列表,实现弹出列表的索引值。

// 先获取到列表项 li
var list = document.querySelectorAll("li");
// 遍历 li 
for (var i = 0; i < list.length; i++) {
  // 给每一个列表项绑定点击事件,打印索引。
  list[i].onclick = function(){
    alert(i);
  }
}

 

一起看起来,好像并没有什么毛病,但是运行的结果却是不管点击那个都是返回最后一个索引值。这是因为在es5中没有块级作用域的原因导致的, 解决的办法很多种举几种常用的解决办法。

1、闭包方式
  var list = document.querySelectorAll("li");
  for (var i = 0; i < list.length; i++) {
    (function(i){
      list[i].onclick = function(){
        console.log(i);
      }
    })(i) 
  }
// 循环内部加一个自执行函数,利用函数作用域,模拟块级作用域。

2、利用let代替var声明, 告诉浏览器使用es6语法
  var list = document.querySelectorAll("li");
    for (let i = 0; i < list.length; i++) {
      list[i].onclick = function(){
        console.log(i);
      } 
    }

3、 使用forEach 循环
  var list = document.querySelectorAll("li");
  list.forEach(function(item,i){
    item.onclick = function(){
      console.log(i);
    }
  })

2,3两种方法跟今天没有关系,这里就不多做讨论。

来一个比较坑的面试题。

function createFunction () {
  var result = new Array();
  for (var i = 0; i < 10; i++) {
    result[i] = function(){
      return i;
    }   
  }
  return result;
}
var aFunction = createFunction ();
aFunction.forEach(function(obj){
  console.log( obj() );
})

大家可以自己尝试着猜猜,结果是什么?

再来一个差不多的面试题。

for (var i = 0; i < 10; i++) {
  setTimeout(function() {
    console.log(i);
  }, 0);
}

 

细心的小伙伴或许已经发现了,这种比较迷惑人的面试题,都有一个共同的特征,就是在循环中包含了一个函数。只要遇到这种问题,都可以用闭包来解决。


3、代替全局变量

我们都知道如果一个程序全局变量太多的话,会影响性能, 那么怎么解决呢,其实最根本的就是把全局变量变成局部变量;

(function(){
  // 对于小范围使用的变量,都可以利用这种方法变成局部变量。
  var name = "HoChine";
  console.log(name);

  function test (){
    console.log(name);
  }
  test();

  /*..... 一堆堆逻辑 .....*/
})()

console.log(name) // name not defined;

 

// 这样的话,在外界就访问不到变量了,避免了全局变量的增多。<p>---恢复内容结束---</p># 闭包

 

 

以上就是我对闭包的理解,如果有错误的地方请提出,欢迎批评指正。一起学习一起进步。

posted @ 2017-03-31 19:46  HoChine  阅读(358)  评论(0编辑  收藏  举报