闭包是如何工作的
闭包是一个函数在创建时允许(自身函数)访问并操作(该自身函数之外的变量)时所创建的作用域。
最简单的闭包:
// 在全局作用域下定义a变量和b函数,b函数(自身函数)能够访问到a变量(该自身函数之外的变量)
// a变量和b函数都是在全局作用域下定义的,此时全局作用域就是一个闭包
var a = 1;
function b() {
console.log(a);
}
-------------------------------------------------------------------
// 在全局作用域下定义一个空变量a,再定义b函数
// 在b函数中,定义变量i和c函数,并将c函数引用到a变量中。
var a;
function b() {
var i = 1;
function c() {
console.log(i);
}
a = c;
}
// 执行b函数,执行结束后b函数的作用域消失
b();
// 再执行a函数,上面有对b函数内部的c函数的引用
// 最终能够输出变量i,
a(); // 1
在第二个例子中,外部函数b声明内部函数c的时候,不仅是声明了函数,还创建了一个闭包,这个闭包不仅包含函数声明,还包含了声明那一时刻点上该作用域中所有的变量,通过这个闭包,c函数就能够访问到外部的i变量。
使用闭包
私有变量
// 定义一个构造函数A,在内部定义一个私有变量b,并定义两个能够访问变量b的方法
function A() {
var b = 0;
this.get = function () {
return b;
};
this.choose = function () {
b++;
}
}
// 实例化A,内部参数只能通过两个给定的方法进行访问,无法直接访问b变量,这样b变量就变成了一个私有变量,无法随意修改
var a = new A();
a.choose();
console.log(a.get()); // 1
回调函数与计时器
// 使用jquery的click函数为元素绑定点击事件时,在回调函数中,success函数能够访问到在其外部定义的$el节点,这就是一个闭包,在setTimeout定时器的回调函数中同理。
$("#testbutton").click(function (){
var $el = $("#text");
$el.html("lodding....");
$.ajax({
url:"test.json",
success:function (dataType) {
var a="";
for(var i in dataType){
var html = i+" "+dataType[i]+"<br>";
a+=html;
}
$el.html(a);
}
});
});
绑定函数上下文
浏览器的事件处理系统中,this的指向会被默认绑定为事件的目标元素,所以在事件回调函数中,使用this要注意这一点。使用bind()方法可以改变this的指向。
<button type="button" id="button">点击</button>
<div id="a">内容A</div>
var fun=function () {
console.log(this);
};
var a = document.querySelector('#a');
var btn = document.querySelector('#button');
// 为按钮绑定点击事件,会打印出按钮本身,但bind()函数中传入了a节点,将fun方法中的this指向了a。
btn.addEventListener("click",fun.bind(a),false); // 打印出a节点
即时函数
即时函数的基本结构:
(function () {
/*函数主题*/
})();
通过参数限制作用域内的名称:
// 在最后执行时将jQuery作为参数传入,在函数开头用$符号接收,在函数内部就可以随意使用$表示jQuery,不受外部干扰,即使外部已经使用过$来表示其他的东西。
var $ = function (){};
(function ($) {
var a = $("#a");
})(jQuery);
即时函数可以解决利用循环和闭包时产生的问题。
// 点击每一个div都会弹出divs的长度,因为闭包记住的是变量的引用,而不是闭包创建时刻该变量的值。最终i的值都是divs的长度。
var divs = document.querySelectorAll('div');
for(var i = 0; i<divs.length; i++){
divs[i].addEventListener('click',function () {
alert(i);
});
}
// 在for循环内加入即时函数,将每次循环后正确的值传入即时函数,事件处理函数就可以得到正确的值
var divs = document.querySelectorAll('div');
for(var i = 0; i<divs.length; i++){
(function (i) {
divs[i].addEventListener('click',function () {
alert(i);
});
})(i);
}