有关闭包
什么是闭包?
定义
- 官方定义:闭包 (closure) 指有权访问另一个函数作用域中变量的函数 。
- 简单理解:闭包就是一个函数,这个函数能够访问另一个函数中的变量
- 注意:被访问的这个变量所在的函数就是闭包函数
作用
最本质的作用就是:延申了变量的作用范围
闭包的原理
高阶函数
闭包就是典型的高阶函数的用法,它内部返回的是一个函数
主要实现原理
- 要使用闭包函数中的局部变量,我们就在闭包函数中 ==return ==一个函数,再在外面调用就可
- 这个局部变量不会立即销毁,因为内部还返回了函数,需要这些函数全部被调用之后再销毁
闭包的应用以及说明
点击 li 获得该 li 的索引号
下面展示一些 内联代码片
。
// 代码
<ul class = 'nav'>
<li>苹果</li>
<li>香蕉</li>
<li>榴莲</li>
</ul>
// 获取所有的li
var lis = document.querySelectorAll('.nav li');
// for 循环给每个li 注册点击事件
// 原理:立即执行函数,将索引号i作为实参传递给立即执行函数,再将这个i打印出来
// 这里的立即执行函数形成了一个小闭包,它里面的变量i 都可以被里面的任何一个函数使用
for(var i = 0; i<lis.length; i++){
(function(i){
lis[i].addEventListener('click',function(){
console.log(i);
})
})(i);
}
打车计价
下面展示一些 内联代码片
。
// 功能说明:起步价13元(3公里内);每增加一公里,就多收5元;如果有拥堵情况,就再加收10元,根据目的地的公里数,自动计算打车价格
var car = (function(){
var start = 13; // 这两个变量在函数内部,所有就是局部变量,但里面的返回函数能访问它,所有这个函数就形成了闭包函数
var total = 0;
return { // 由于需要返回两个函数,我们利用在对象中添加方法这种方式
price: function(n){
if(n>=3){
total = start + (n-3)*5;
}
else{
total = start;
}
return total
},
YD: function(flag){
return flag = true? total+10 :total
}
}
})()
// 调用函数
console.log(car.price(5));
console.log(car.YD(ture));
这两个小案例中都用到了闭包,使局部变量也能被其他函数访问,延申了变量的作用域,节约了内存空间。
思考题:下面案例中有没有用到闭包
案例1
下面展示一些 内联代码片
。
var name = 'The Window';
var o = {
name:'My object',
getName: function(){
return function(){
return this.name;
};
}
};
console.log(o.getName()());
// 问题1? 这里的this 指向什么?结果是什么
相当于指向了以下步骤:
var f = o.getName();
// 对象的方法是返回一个函数
f = function(){
return this.name;
};
f();
function(){this}(); // 这就是立即执行函数,而立即执行函数中的This指向的是window
// 因此输出的结果应该是全局变量的Name “The Window’
以上输出的结果应该是全局变量的Name “The Window“
这里没有闭包的产生。
案例2
下面展示一些 内联代码片
。
// An highlighted block
var name = 'The Window';
var object = {
name : 'my object';
getName:function(){
var that = this; // 对象中的方法中的this指向的是函数的调用者,而根据 object.getName() 我们得知是对象调用了这个函数,因此this 指向object
return function(){
return that.name;
};
}
};
console.log(object.getName()());
// 和以上分析一样,只不过这里的this 指向的不window 而是object
以上输出的结果是‘my object’
因为返回的函数里面用到了getName 这个函数中的变量,所有用到了闭包,getName()就是闭包函数。
谈谈this 的指向问题
普通函数 : this指向window
对象中的方法: this指向方法的调用者,一般是所在的这个对象
构造函数 : this指向的是一个实例对象
绑定事件函数(按钮绑定点击事件) : this 指向函数的调用者
定时器函数 : this 一定指向的是window
立即执行函数 : 和普通函数一样指向window
函数 | this指向 |
---|---|
普通函数 | window |
对象中的方法 | 方法的调用者,一般是所在的这个对象 |
构造函数 | 一个实例对象 |
绑定事件函数(按钮绑定点击事件) | 函数的调用者 |
定时器函数 | window |
立即执行函数 | 和普通函数一样指向window |
改变this 的指向问题
一般来说,This 的指向是固定的,在实际开发需求中,我们往往想要改变this 的指向,更利于开发
改变this 指向有三种方法:
-
call () : 主要应用于继承;
-
apply() : 由于它传递的参数必须是数组形式或者伪数组 形式,所有它主要应用于跟数组有关的场景
3.== bind()== : 应用于我们想要改变this 的指向而又不想让这个函数立即调用,比如定时器,定时器函数就是在时间到了才调用函数的, -
对于apply() 的主要应用:我们来用它求一个数组中的最大值
-
apply(指向目标,数组1,数组2,…)
// An highlighted block
var arr = [1,66,0,89,234]
var max = Math.max.apply(Math,arr);
// 这里的apply(Math,arr)中的Math 代表不改变this的指向,还是指向原来的Math 这个内置对象,也可以写成null 但是严格模式下不建议写null,
console.log(max);
下面来重点说说这个bind() 它也是在实际开发中用的最多的方法;
问题描述:
我们有三个按钮,当点击按钮,被点击的按钮禁用了,3秒之后又开启:
我们用两种方法来对比效果,看看哪种方式更灵活
- 传统方法
下面展示一些内联代码片
。
// Body部分
<button>key1</button>
<button>key2</button>
<button>key3</button>
// script部分
<script>
var btns = document.querySelectorAll('button');
// for 循环绑定事件
for(var i=0; i<lis.length; i++){
btns[i].onclick = function(){
this.disabled = true; // 点击就禁用
setInterval(function(){
btns[i].disabled = false // 注意这里只能写btns[i] 绝对不能写this, 这里的this 指向的是window
},3000)
}
}
</script>
- 用bind() 方法
// Body部分
<button>key1</button>
<button>key2</button>
<button>key3</button>
// script部分
<script>
var btns = document.querySelectorAll('button');
// for 循环绑定事件
for(var i=0; i<lis.length; i++){
btns[i].onclick = function(){
this.disabled = true; // 点击就禁用
setInterval(function(){
this.disabled = false //这里的this 来源于Bind传递的参数
}.bind(this),3000) // 这里的this 在定时器的外面,指向的不是window ,而是函数的调用者btns[i],把这个this 传递给定时器函数
}
}
</script>
方法2 改变了this 的指向,原本定时器函数中的this 指向的window ,这里利用bind 将this 指向了绑定事件的对象–按钮!
nice!!