闲着无聊,写下闭包函数的一个小记
闭包函数是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还是要抓紧用到实战用啊,确定很方便的
还有就是闭包函数的使用其实并不会主动释放内存,所以在使用闭包函数的时候应该注意不能滥用。