闭包是指有权访问另一个函数作用域中变量的函数
其实闭包很常见,甚至于每个js初学者就接触过(for循环加事件等都使用了闭包的概念)。
下面代码1是一个最简单的闭包案例
//代码1
function funA(){
var a = 10; // funA的活动对象之中;
return function(){ //匿名函数的活动对象;
console.log(a);
}
}
var b = funA();
b(); //10
//在funA中定义了一个内部函数,然后在内部函数调用了funA()的对象
代码2这个与代码1类似,只不过代码1返回一个匿名函数。
//代码2
function fn1() {
var num = 10;
function fn2() {
console.log(num);
}
return fn2; //在这里返回fn2函数
}
var f = fn1(); //实现fn1外面的作用域可以访问到fn1内部的局部变量
f(); //当调用f()则会有fn2函数的返回
//类似于
//var f = function fn2() {
// console.log(num);
// }
无处不在的闭包
//代码3
//定时器
function wait(message) {
setTimeout(function timer() {
console.log(message)
}, 1000)
}
wait('hello 闭包')
//点击事件处理
var lis = document.getElementsByTagName("li");
for(var i=0;i<lis.length;i++){
(function(i){
lis[i].onclick = function(){
console.log(i);
};
})(i);
}
//jQuery
function some(selector,What) {
$(selector).onClick(function(){
console.log(What)
})
}
some('#dom1','what');
some('#dom2','what2');
//ajax
function foo(someData){
$.ajax({
data:data,
url:url,
success:function(result){
console.log(someData)
}
})
}
foo('我也是闭包')
值得一提的是通过这次讲课我还get到了一个之前接触过,但不太关心的东西,叫立即执行函数。并且更加深入的了解了let,var的区别。
立即执行函数
格式大概为
(function(){
})()
代码4用立即执行函数实现了闭包的特点
//代码4
var a = 2;
(function(){
console.log(a) //2
})()
//虽然它能够正确的执行,但是严格意义上来说,它并不是一个闭包,
// 因为它不是在本身的词法作用域之外执行的。
// 它确确实实产生了闭包的效果,因此也有种说法叫他为小闭包。
* 立即执行函数和闭包的区别
*
立即执行函数和闭包没有关系,虽然两者会经常结合在一起使用,但两者有本质的不同
立即执行函数只是函数的一种调用方式,只是声明完之后立即执行,这类函数一般都只
是调用一次(可用于单例对象上),调用完之后会立即销毁,不会占用内存
闭包则主要是让外部函数可以访问内部函数的作用域,也减少了全局变量的使用,
保证了内部变量的安全,但因被引用的内部变量不能被销毁,增大了内存消耗,
使用不当易造成内存泄露
*/
let与var
//代码5
function fn1(){
var arr = [];
for(var i = 0;i < 5;i ++){
console.log("i---"+i);
arr[i] = function(){
return i;
}
}
return arr;
}
var list = fn1();
for(var i = 0,len = list.length;i < len ; i ++){
console.log(list[i]());
}
//猜猜上面函数输出什么 答案:55555
//答案向右边拉
//下面的函数又输出什么呢 答案:01234
function fn2(){
var arr = [];
for(let i = 0;i < 5;i ++){
console.log("i---"+i);
arr[i] = function(){
return i;
}
}
return arr;
}
var list = fn2();
for(var i = 0,len = list.length;i < len ; i ++){
console.log(list[i]());
}
//这两者就是let和var的区别
简单说一下我对这个的理解,由于var是先声明后赋值的,(即在js里面,每一个var对象都在最上面先声明的,然后再在你写代码的地方赋值,即全局变量),代码5上面那个fn1函数当执行函数的时候for循环已经循环结束了,即 i 已经为5了,所以说每一个输出都为5,但fn2,定义的 i 为let,而每个for循环里面的 i 都互不影响(可以理解为有5个不同的i),所以说每次调用list[i]的时候都输出对应索引的 i 值。
(以上为个人理解,如果有错误或者描述不清楚的地方。欢迎指出,补充/)
最后再放两道我听课时候没仔细听的题
//代码6
<!--需求:点击每一个li,打印出其对应的索引号-->
<ul class="nav">
<li>榴莲</li>
<li>臭豆腐</li>
<li>鲱鱼罐头</li>
<li>大猪蹄子</li>
</ul>
<script>
//闭包案例-点击li输出索引号
//1.利用原先动态添加属性的方式
var lis = document.querySelector('.nav').querySelectorAll('li');
// for (var i=0;i<lis.length;i++){
// lis[i].index = i; //动态添加
// lis[i].onclick = function() {
// //无法直接打印console.log(i);由于此时的function()为异步任务,for循环为同步任务,则先会执行,输出结果只能为4
// // console.log(i);
// console.log(this.index)
// }
// }
//利用闭包的方式得到当前li的索引号
for (var i=0;i<lis.length;i++){
//立即执行函数:利用for循环创建了若干个立即执行函数
//立即执行函数也可以称为一个小闭包因为其内部任何一个函数都可以使用i这个变量
(function (i) { //此处为接收i
// console.log(i);
lis[i].onclick = function() {
console.log(i);
}
})(i); //最后一个小括号为调用,则其可接受一个参数的传入,传入i
}
//代码7
//闭包案例-打车价格
//打车起步价13(3公里内),之后每公里增加五元,用户输入公里数计算打车价格
//如果有拥堵情况,总价格多收取10元拥堵费
// function fn() {}
// fn();
var car = (function () {
var start = 13; //起步价:局部变量
var total = 0; //总价:局部变量
return {
price: function (km) { //正常的总价
if(km<=3){
total = start;
}else{
total = start+(km-3)*5;
}
return total;
},
congestion: function (flag) { //拥堵之后的费用
return flag ? total+10 :total;
}
}
})();
console.log(car.price(5)); //23
console.log(car.congestion(true)); //33
console.log(car.price(1)); //13
console.log(car.congestion(false)); //13
</script>