闭包
闭包
1.闭包的概念
MDN中描述,闭包是由函数以及声明该函数的词法环境组合而成的。该环境包含了这个闭包创建时作用域内的任何局部变量。
function init() {
var name = "Mozilla"; // name 是一个被 init 创建的局部变量
function displayName() { // displayName() 是内部函数,一个闭包
alert(name); // 使用了父函数中声明的变量
}
displayName();
}
init();
比如执行displayName(),diplayName的函数通过作用域链(
了解js的执行过程:https://www.cnblogs.com/listenMao/p/13329620.html
)得到name的值。通过 displayName()我们可以保存name的信息,这有赖于作用域链。js的词法作用域的特点是在函数创建定义时确定了作用域链,当一个内部函数(如displayName)变成一个闭包时,拥有 “他自身(如displayName)所形成的作用域链“。
2.闭包的缺点
function makeFunc() {
var name = "Mozilla";
function displayName() {//闭包
alert(name);
}
return displayName;
}
var myFunc = makeFunc();//myFunc指displayName;
myFunc();
.通常,函数的作用域及其所有变量都会在函数(makeFunc)执行结束后被销毁。但是,在创建了一个闭包(displayName)以后,这个函数的作用域(makeFunc)就会一直保存到闭包不存在为止。存在占用内存。
myFunc=null;//释放内存
3.闭包的使用
3.1模拟面向对象的方式
使用闭包来定义公共函数,并令其可以访问私有函数和变量,实际上可以使用es6的类或者es5的构造函数
var person=function(){
//定义私有
var name="";
return {//暴露公共函数
getName:function(){//
return name;
},
setName:function(val){
name=val;
}
}
}
//建了一个词法环境,为2个函数共享。这2个公共函数是共享同一个环境的闭包。
var p=person();p.setName(6);p.getName();
3.2解决循环中类似输出i的错误
//例子1
//假设3个button,items为获取到的对应dom元素
var items=document.getElementsByTagName("button");
var len=items.length;
for (var i = 0; i < len; i++) {
items[i].onclick = function () {
console.log(i);//2,2,2
}
}
//闭包解决
//这一种是立即执行形成闭包
for(var i = 0; i < len; i++) {
(function(i){
items[i].onclick = function () {
console.log(i);//1,2,3
}
})(i);
}
//这一种是函数工厂,所有的回调不再共享同一个环境, make 函数为每一个回调创建一个新的词法环境
var make=function(i){
return function () {//这里不能是有参数i
console.log(i);//1,2,3
}
}
for(var i = 0; i < len; i++) {
items[i].onclick =make(i);
}
//例子2
for( var i=0;i<3;i++){
setTimeout(function(){
console.log(i); // 3,3,3
}
,300);
}
//闭包解决
for( var i=0;i<3;i++){
(function(i){
setTimeout(function(){
console.log(i); // 1,2,3
}
,300);
})(i);
}
参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures