- 闭包就是一个函数里面嵌套一个函数,里面的函数通过return返回出来。
2.下面创建了4个闭包函数,每个调用都独立,互不干扰。
var num = new Array();
for(var i=0; i<4; i++){
//num[i] = 闭包;//闭包被调用了4次,就会生成4个独立的函数
//每个函数内部有自己可以访问的个性化(差异)的信息
num[i] = f1(i);
}
function f1(n){
return function f2(m){
n=n++;
m=m++;
alert(n+m);
}
}
num[0](2); //alert(2)
num[0]=null; //释放内存
二、下面是一个经典例子,闭包中变量的变化。
function fn(){
var n= 0
return function(){
var m = 0;
console.log(++n,++m)
}
}
var fn1 = fn()
fn1() //console 1,1
fn1() //console 2,1
三、下面3种情况,慢慢体会
for (var i = 0; i<5; i++){
setTimeout(function(){console.log},100)
}
//会打印5个5
for(var i = 0; i<5; i++){
(function(i){
setTimeout(function(){console.log(i)},100)
}(i))
}
//用闭包,把变量放在上级,就会打印0,1,2,3,4
//直接用let也可以实现这种方法
for(var i = 0; i<5; ++i){
(function(i){
setTimeout(function(){console.log(i)},1000*i)
}(i))
}
//每隔1秒执行一次定时器
四、闭包作为参数传递
var num = 11
var fn1 = function(x){
if(x>num){
console.log(x)
}
}
void function(fn2){
var num = 100
fn2(30)
}(fn1)
//console 30
//fn1函数作为参数传递进立即执行函数,fn2(30)把30传递进fn1函数,然后执行fn1函数里面的判断。fn1里面的num作用域取的是全局作用域的num
五、闭包的常见数组按钮写法
某段dom结构如下:在该dom结构中,li在列表的下标分别是0,1,2,3,4.请分别为每个li添加点击事件,输出响应的下标,注意:使用原生的js,且只能添加事件不能添加属性.
<ul id="list">
<li>qw</li>
<li>er</li>
<li>ty</li>
<li>ui</li>
<li>ou</li>
</ul>
var lis = document.querySelectorAll('li');
//闭包实现
for (var i = 0; i < lis.length; i++) {
lis[i].onclick = (function (j) {
return function () {
console.log(j);
}
})(i);
}
//forEach实现
lis.forEach(function (v, i) {
v.onclick = function () {
console.log(i);
}
})
//ES6实现
for(let i=0;i<lis.length;i++){
lis[i].onclick=function(){
console.log(i);
}
}
最后总结一下闭包的好处与坏处
好处
①保护函数内的变量安全 ,实现封装,防止变量流入其他环境发生命名冲突
②在内存中维持一个变量,可以做缓存(但使用多了同时也是一项缺点,消耗内存)
③匿名自执行函数可以减少内存消耗
坏处
①其中一点上面已经有体现了,就是被引用的私有变量不能被销毁,增大了内存消耗,造成内存泄漏,解决方法是可以在使用完变量后手动为它赋值为null;
②其次由于闭包涉及跨域访问,所以会导致性能损失,我们可以通过把跨作用域变量存储在局部变量中,然后直接访问局部变量,来减轻对执行速度的影响
防抖函数中用闭包,储存一个变量函数
// 防抖
function debounce(fn, wait) {
var timeout = null;
return function() {
if(timeout !== null) clearTimeout(timeout);
timeout = setTimeout(fn, wait);
}
}
// 处理函数
function handle2() {
console.log("防抖函数");
}
// 滚动事件
window.addEventListener('scroll', debounce(handle2, 1000));
节流函数
var throttle = function(func, delay) {
var timer = null;
return function() {
var context = this;
var args = arguments;
if (!timer) {
timer = setTimeout(function() {
func.apply(context, args);
timer = null;
}, delay);
}
}
}
let func = function() {
console.log("节流函数");
}
window.addEventListener('scroll', throttle(func, 1000));
单例模式
意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要解决:一个全局使用的类频繁地创建与销毁。
var HeadClass = function () { };
var Head = (function () { // 匿名自执行函数
var instance; // 声明一个instance对象
return function () {
if (instance) { // 如果已存在 则返回instance
return instance;
}
instance = new HeadClass() // 如果不存在 则new一个HeadClass对象
return instance;
}
})();
var a = new Head();
var b = new Head();
console.log(a===b) // true
柯里化 currying
function Parent(x,y){
return x+y
}
function curryingParent(x){
return function(y){
return x+y
}
}
curryingParent(1)(2)