闭包
什么是闭包?
闭包是一个函数和声明该函数的词法环境的组合,这个词法环境包含了嵌套函数所能访问的所有局部变量,嵌套的函数可以访问其外部环境中的变量。
通常情况下,闭包的形式如下:
function parentFunc(){
//变量声明
......
return function childFunc(){
......
}
}
内部函数使用外部函数的变量时,它会保留这些变量的引用,即使外部函数已经执行完毕,变量依然可以访问。
var createCounter = function(n) {
var previous = n;
return function() {
//执行顺序:1) return previous; 2) previous++;
return previous++;
};
};
/**
* const counter = createCounter(10)
* counter() // 10
* counter() // 11
* counter() // 12
*/
闭包的使用
相同算法不同参数
例如如果要计算公司内部组长、下属的薪酬,用的是同一种算法,但是每种员工的工资基数不同:
function wages(x){
return function bonus(y){
return x+y;
}
}
var leader=wages(20000)(2000); //22000
var staff=wages(10000)(1000); //11000
模拟私有方法
使用闭包模拟私有方法,对于一些方法不想公开时可以使用闭包:
暂时还不太懂用法
循环事件赋值回调
将HTML元素循环遍历,并定义每一个的点击事件,很容易就会写成下面的样子:
function showHelp(help) {
document.getElementById('help').innerHTML = help;
}
function setEvent(){
var helpText = [
{'id': 'email', 'help': 'Your e-mail address'},
{'id': 'name', 'help': 'Your full name'},
{'id': 'age', 'help': 'Your age (you must be over 16)'}
];
for(var i=0,len=helpText.length;i<len;i++){
var item=helpText[i] //var 或 let 都一样是helpText[2]
document.getElementById(helpText[i].id).onfocus=function(){showHelp(item.help)}
}
}
setEvent()
运行的结果是每一个HTML的onfocus事件触发时都显示 ‘Your age (you must be over 16)’。
这是因为上面的 setEvent 方法中,var声明的 i 作用域为function setEvent,var没有块级作用域,因此在触发onfocus事件时i==2此时就输出helpText[2]。
那么,如何才能实现呢?有三种方法:
- 将 i 的声明改成let ,因为let (有块级作用域)声明时 i 的作用域是在for{}每一次循环的代码块中,因此在触发onfocus时 每一次 item 都是随着 i 重新定义,所以onfocus事件中的 i 是当前代码块中的 i 。
- 再使用一次闭包:
function showHelp(help) {
document.getElementById('help').innerHTML = help;
}
function secondClosure(help){
return function(){
return showHelp(help)
}
}
function setEvent(){
var helpText = [
{'id': 'email', 'help': 'Your e-mail address'},
{'id': 'name', 'help': 'Your full name'},
{'id': 'age', 'help': 'Your age (you must be over 16)'}
];
for(var i=0,len=helpText.length;i<len;i++){
var item=helpText[i]
//调用secondClosure,参数help指向每次循环时的helpText[i].help,调用时就会正确传入help
document.getElementById(helpText[i].id).onfocus=secondClosure(helpText[i].help)
}
}
或者使用立即执行函数:
function setEvent(){
var helpText = [
{'id': 'email', 'help': 'Your e-mail address'},
{'id': 'name', 'help': 'Your full name'},
{'id': 'age', 'help': 'Your age (you must be over 16)'}
];
for(var i=0,len=helpText.length;i<len;i++){
(function(){
var item=helpText[i]
document.getElementById(helpText[i].id).onfocus=
function(){showHelp(helpText[i].help)}
})()
}
}
闭包的坏处
在特殊情况下使用闭包,但是一般不建议用闭包,因为这样的方式是将方法定义在对象或类的构造器上,每次创建对象时方法会被重新创建,这对内存损耗,处理速度上很不利。可以将方法添加至对象或类的原型上:
function A(name,age){
this.money=1000
this.name=name
this.age=age
}
A.prototype.getMoney=function(){
return this.money
}