1.变量的作用域
js的变量分为两种:全局变量和局部变量
全局变量就是不管在哪都可以访问到的变量,局部变量是只有局部代码块可以访问到。
// num 全局变量 ,num1 局部变量
var num = 20;
function a() {
var num1 = 1000;
console.log(num); // 20
console.log(num1); // 1000
}
a();
console.log(num1); //num1 is not defined
2.闭包的概念
闭包在JavaScript高级程序设计(第3版)的定义是:闭包是指有权访问另一个函数作用域中的变量的函数。
简单借用别人例子说明一下:
function f2() {
var a = 0;
return function() {
return a++; //内部函数可以访问f2作用域中的变量,这个函数就是一个闭包
}
}
var f = f2();
// 当f再次被调用的时候,执行return 后面的函数
// 而 f2 定义的变量对于return后的函数来说,相当于全局变量
console.log(f());
console.log(f());
console.log(f());
// 只有f2重新被调用的时候,局部变量 a = 0
f = f2();
console.log(f());
console.log(f());
3.闭包的简单应用
准备实现的功能:给ul中每一个li添加一个点击事件,当点击的时候可以打印出它对应的序列号.
ES6 中 let 原理也是采用了闭包
详情请看参考文献二
// 常规做法:给每一个li 添加一个index 属性
// 或者使用let定义i,let具有块级作用域
var lis = document.querySelectorAll('li');
for(var i=0;i<lis.length;i++){
// lis[i].index = i;
lis[i].onclick = function(){
console.log(i+1);
}
}
// 用闭包实现:可以在外部函数里面绑定li 点击事件,将i作为形参传给内部函数
// 而外部函数是立即执行函数,会再调用内部函数
for(var i=0;i<lis.length;i++){
(function(i){
lis[i].onclick = function(){
console.log(i);
}
})(i)
}
4.闭包的应用-事件节流和防抖
事件节流:一次函数执行后,只有大于指定的执行周期内才能执行第2次
简单的说:就是在规定时间内,让函数第一次触有效,后面触发不生效。
实现效果:给点击按钮绑定一个事件节流函数,使按钮在第一次触发生效,在规定时间内不生效。
// 节流(用时间戳)的原始做法:
function f1(){
console.log(Date.now());
}
var startTime = 0; //依靠全局变量
function throttle(fn,delay){
var nowTime = Date.now();
if(nowTime - startTime > delay){
fn();
startTime = nowTime;
}
}
btn.onclick = function() {
throttle(f1,1000);
}
// 将节流封装成一个方法去调用
function throttle(fn,delay){
var startTime = 0;
return function() {
var nowTime = Date.now();
if(nowTime - startTime > delay){
fn.call(this); // 如果不改变fn 的this指向,fn里面的this最终被指向window
}
startTime = nowTime;
}
}
btn.onclick = throttle(f1,1000);
事件防抖:一个高频触发的函数,在规定的时间内只在最后一次才能执行函数。
实现效果:多次点击在规定的1秒内只有最后一次有效果
应用:微信点赞、实时搜索
// 事件防抖 原始做法
var timer = null;
function debounce(fn,delay) {
clearTimeout(timer);
timer = setTimeout(function(){
fn();
},delay)
}
btn.onclick = function(){
debounce(f1,1000);
}
// 使用闭包 封装的做法
function debounce(fn,delay){
var timer = null;
return function(){
clearTimeout(timer);
timer = setTimeout(function(){
fn.call(this);
}.bind(this),delay)
// 如果不改变this 指向,this 指向 window,并不是指向被调用的btn
}
}
btn.onclick = debounce(f1,1000);
闭包的优缺点
优点:
- 延长了变量的作用域;
- 避免了全局变量污染;
- 不会被垃圾回收机制回收
缺点:
占有了内存
参考文献
[1]http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
[2]https://blog.csdn.net/weixin_46726346/article/details/109153742?spm=1001.2014.3001.5502
[3]https://blog.csdn.net/cauchy6317/article/details/81167572?spm=1001.2014.3001.5501