Bootstrap

闭包函数调用小记

 

闲着无聊,写下闭包函数的一个小记

闭包函数是js的一个重点,可以说没有闭包函数,js的面向对象的实现思路无法实现。但是我们经常会忽略闭包函数作用域的问题,导致页面结果跟预想不一样。一个很经典的例子就是输入框问题:

<p id="showid">显示焦点元素</p>
<p>Name: <input type="text" id="name"></p>
<p>E-mail: <input type="text" id="email"></p>
<p>Age: <input type="text" id="age"></p>

我们希望可以在输入法获取焦点的时候,可以实时的展示出来当前获取到焦点的元素是哪一个。这个实现起来很简单,我们可以用最直白的方式:

document.getElementById('name').onfocus = function() {
      document.getElementById('showid').innerHTML = 'name';
    }
document.getElementById('email').onfocus = function() {
      document.getElementById('showid').innerHTML = 'email';
    }
document.getElementById('age').onfocus = function() {
      document.getElementById('showid').innerHTML = 'age';
    }

这样是可以实现要求,但是这种实现方式是很死板的,而且可拓展性病不是很好。

我们可以使用函数的形式,对上面的代码进行封装,做到阅读性和可拓展性的增强。

首先,我们写个展示的函数

function showiddom(res) {
  document.getElementById('showid').innerHTML = res;
}

然后就是事件的监听了

function setshowid() {
  var Text = [
      {'id': 'email', 'showcont': '正在输入邮箱'},
      {'id': 'name', 'showcont': '正在输入姓名'},
      {'id': 'age', 'showcont': '正在输入年龄'}
    ];

  for (var i = 0; i < Text .length; i++) {
    var item = Text [i];
    document.getElementById(item.id).onfocus = function() {
      showiddom(item.showcont);
    }
  }
}

setshowid();

这样就完成了,我们通过循环数组完成对每个输入框的监听。但是当我们运行的时候你会发现,无论焦点在那个输入框总是显示正在输入年龄,这和我们想要的结果完全不一样啊。

这是要是因为我们使用循环的方式去实现监听onfocus的时候,三次循环中onfocus都是用的闭包函数,在执行各自的showiddom的时候都有各自的作用域,他们捕获到的是一个共同的变量item,所以在执行的时候总是获取的最后一个循环的变量值。

这就是闭包的滥用问题,那我们怎么样才能解决这个问题呢?主要还是解决作用域的问题。

1、使用更多层的闭包

我们把showiddom再做一层闭包处理

function showidCallback(res) {
  return function() {
    showiddom(res);
  };
}
for (var i = 0; i < Text.length; i++) {
    var item = Text[i];
    document.getElementById(item.id).onfocus = showidCallback(item.showcont);
  }

通过对showiddom的再一次封装,在循环调用的时候就不会指向同一个作用域了,showidCallback函数为每一个回调创建一个新的词法环境,这新的环境中,res就会指向对应数组中的值了。

2、隐匿函数立刻执行

function setshowid() {
  var Text = [
      {'id': 'email', 'showcont': '正在输入邮箱'},
      {'id': 'name', 'showcont': '正在输入姓名'},
      {'id': 'age', 'showcont': '正在输入年龄'}
    ];

  for (var i = 0; i < Text .length; i++) {
    var item = Text [i];
    (function(){
       document.getElementById(item.id).onfocus = function() {
          showiddom(item.showcont);
       } 
    })();
   
  }
}

这样通过隐匿函数,在循环的时候立刻就可以把item和事件关联了起来,也可以达到我们的效果。

3、使用let

let是用来声明局部变量的,也就是指对当前代码块有用,这个刚好可以解决作用域的问题。

 for (var i = 0; i < Text .length; i++) {
    let item = Text [i];
    document.getElementById(item.id).onfocus = function() {
      showiddom(item.showcont);
    }
  }

不得不说es6还是要抓紧用到实战用啊,确定很方便的

还有就是闭包函数的使用其实并不会主动释放内存,所以在使用闭包函数的时候应该注意不能滥用。

;